~ubuntu-branches/ubuntu/quantal/mysql-workbench/quantal

« back to all changes in this revision

Viewing changes to backend/wbprivate/sqlide/wb_sql_editor_form.cpp

  • Committer: Package Import Robot
  • Author(s): Dmitry Smirnov
  • Date: 2012-03-01 21:57:30 UTC
  • Revision ID: package-import@ubuntu.com-20120301215730-o7y8av8y38n162ro
Tags: upstream-5.2.38+dfsg
ImportĀ upstreamĀ versionĀ 5.2.38+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
 * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
 
3
 *
 
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
 
7
 * License.
 
8
 * 
 
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.
 
13
 * 
 
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
 
17
 * 02110-1301  USA
 
18
 */
 
19
 
 
20
#include "stdafx.h"
 
21
 
 
22
#include "wb_sql_editor_form.h"
 
23
 
 
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"
 
31
 
 
32
#include "objimpl/db.query/db_query_Resultset.h"
 
33
 
 
34
#include "workbench/wb_context_ui.h"
 
35
 
 
36
#include "sqlide/sql_script_run_wizard.h"
 
37
 
 
38
#include "base/file_functions.h"
 
39
#include "base/log.h"
 
40
 
 
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>
 
46
 
 
47
#include "base/boost_smart_ptr_helpers.h"
 
48
 
 
49
#include <boost/foreach.hpp>
 
50
#include <boost/scoped_ptr.hpp>
 
51
#include <boost/signals2/connection.hpp>
 
52
#include "grt/common.h"
 
53
 
 
54
#include "query_side_palette.h"
 
55
 
 
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"
 
62
 
 
63
using namespace bec;
 
64
using namespace grt;
 
65
using namespace wb;
 
66
using namespace base;
 
67
 
 
68
using boost::signals2::scoped_connection;
 
69
 
 
70
DEFAULT_LOG_DOMAIN("SqlEditor")
 
71
 
 
72
static const char *SQL_EXCEPTION_MSG_FORMAT= _("Error Code: %i\n%s");
 
73
static const char *EXCEPTION_MSG_FORMAT= _("Error: %s");
 
74
 
 
75
#define CATCH_SQL_EXCEPTION_AND_DISPATCH(statement, log_message_index, duration) \
 
76
catch (sql::SQLException &e)\
 
77
{\
 
78
  set_log_message(log_message_index, DbSqlEditorLog::ErrorMsg, strfmt(SQL_EXCEPTION_MSG_FORMAT, e.getErrorCode(), e.what()), statement, duration);\
 
79
}
 
80
 
 
81
#define CATCH_EXCEPTION_AND_DISPATCH(statement) \
 
82
catch (std::exception &e)\
 
83
{\
 
84
  add_log_message(DbSqlEditorLog::ErrorMsg, strfmt(EXCEPTION_MSG_FORMAT, e.what()), statement, "");\
 
85
}
 
86
 
 
87
#define CATCH_ANY_EXCEPTION_AND_DISPATCH(statement) \
 
88
catch (sql::SQLException &e)\
 
89
{\
 
90
  add_log_message(DbSqlEditorLog::ErrorMsg, strfmt(SQL_EXCEPTION_MSG_FORMAT, e.getErrorCode(), e.what()), statement, "");\
 
91
}\
 
92
CATCH_EXCEPTION_AND_DISPATCH(statement)
 
93
 
 
94
#define CATCH_ANY_EXCEPTION_AND_DISPATCH_TO_DEFAULT_LOG(statement) \
 
95
catch (sql::SQLException &e)\
 
96
{\
 
97
  _grtm->get_grt()->send_error(strfmt(SQL_EXCEPTION_MSG_FORMAT, e.getErrorCode(), e.what()), statement);\
 
98
}\
 
99
catch (std::exception &e)\
 
100
{\
 
101
  _grtm->get_grt()->send_error(strfmt(EXCEPTION_MSG_FORMAT, e.what()), statement);\
 
102
}
 
103
 
 
104
 
 
105
class Timer
 
106
{
 
107
public:
 
108
  Timer(bool run_immediately) : _is_running(false), _start_timestamp(0), _duration(0)
 
109
  {
 
110
    if (run_immediately)
 
111
      run();
 
112
  }
 
113
  void reset()
 
114
  {
 
115
    _is_running= false;
 
116
    _start_timestamp= 0;
 
117
    _duration= 0;
 
118
  }
 
119
  void run()
 
120
  {
 
121
    if (_is_running)
 
122
      return;
 
123
    _is_running= true;
 
124
    _start_timestamp= timestamp();
 
125
  }
 
126
  void stop()
 
127
  {
 
128
    if (!_is_running)
 
129
      return;
 
130
    _is_running= false;
 
131
    _duration+= timestamp() - _start_timestamp;
 
132
  }
 
133
  time_t duration()
 
134
  {
 
135
    return _is_running ? (_duration + timestamp() - _start_timestamp) : _duration;
 
136
  }
 
137
  std::string duration_formatted()
 
138
  {
 
139
    std::div_t s;
 
140
    s= std::div((int)duration(), 1000);
 
141
    return strfmt(_("%i.%.3i sec"), s.quot, s.rem);
 
142
  }
 
143
private:
 
144
  bool _is_running;
 
145
  time_t _start_timestamp;
 
146
  time_t _duration;
 
147
};
 
148
 
 
149
/*
 
150
std::string & strip_sql_ident(std::string &text)
 
151
{
 
152
  std::locale loc;
 
153
 
 
154
  size_t size= text.size();
 
155
 
 
156
  size_t start= 0;
 
157
  for (; start < size; ++start)
 
158
    if (!std::isspace(text[start], loc) && (text[start] != '`') && (text[start] != '\'') && (text[start] != '"'))
 
159
      break;
 
160
 
 
161
  size_t end= size;
 
162
  for (; end > 0; --end)
 
163
    if (!std::isspace(text[end-1], loc) && (text[end-1] != '`') && (text[end-1] != '\'') && (text[end-1] != '"'))
 
164
      break;
 
165
 
 
166
  return text= text.substr(start, end-start);
 
167
}*/
 
168
 
 
169
 
 
170
SqlEditorForm::Ref SqlEditorForm::create(wb::WBContextSQLIDE *wbsql, const db_mgmt_ConnectionRef &conn)
 
171
{
 
172
  SqlEditorForm::Ref instance(new SqlEditorForm(wbsql, conn)); 
 
173
  
 
174
  instance->get_base_schema_tree()->set_delegate(instance);
 
175
  instance->get_base_schema_tree()->set_fetch_delegate(instance);
 
176
  
 
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());
 
180
 
 
181
  if (conn.is_valid())
 
182
  {
 
183
    //instance->connect();
 
184
    //instance->finish_startup();
 
185
  }
 
186
  return instance;
 
187
}
 
188
 
 
189
 
 
190
SqlEditorForm::SqlEditorForm(wb::WBContextSQLIDE *wbsql, const db_mgmt_ConnectionRef &conn)
 
191
  :
 
192
  _wbsql(wbsql),
 
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!
 
196
  _toolbar(NULL),
 
197
  _autosave_lock(NULL),
 
198
  _autosave_disabled(false),
 
199
  _loading_workspace(false),
 
200
  _close_done(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),
 
206
  _connection(conn),
 
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())),
 
215
  _side_bar(NULL),
 
216
  _side_splitter(NULL), _info_tabview(NULL), _object_info(NULL), _session_info(NULL),
 
217
  _side_palette_host(NULL),
 
218
  _side_palette(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()))
 
223
{  
 
224
  _options= DictRef::cast_from(_grtm->get_grt()->get("/wb/options/options"));
 
225
 
 
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");  
 
229
 
 
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();
 
235
 
 
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);
 
238
 
 
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));
 
241
 
 
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);
 
246
 
 
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, ""));
 
249
 
 
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, ""));
 
252
 
 
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, ""));
 
255
 
 
256
  _last_log_message_timestamp= timestamp();
 
257
 
 
258
  _progress_status_update_interval= _options.get_int("DbSqlEditor:ProgressStatusUpdateInterval", 500);
 
259
 
 
260
  int keep_alive_interval= _options.get_int("DbSqlEditor:KeepAliveInterval", 600);
 
261
  if (keep_alive_interval != 0)
 
262
  {
 
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));
 
265
  }
 
266
 
 
267
  _continue_on_error= (_options.get_int("DbSqlEditor:ContinueOnError", 0) != 0);
 
268
}
 
269
 
 
270
 
 
271
void SqlEditorForm::finish_startup()
 
272
{
 
273
  // this part was in do_connect(), but moved here since they need to be called
 
274
  // from main thread anyway
 
275
  //>
 
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);
 
281
 
 
282
  //live_schemata_refresh_task->execute_in_main_thread(
 
283
  //  boost::bind((void(SqlEditorForm::*)(bool))&SqlEditorForm::refresh_schema_tree, this),
 
284
  //  false,
 
285
  //  true);
 
286
  refresh_schema_tree(true);
 
287
  update_menu_and_toolbar();
 
288
  //<
 
289
 
 
290
  setup_sidebars();
 
291
 
 
292
  std::string name = _connection->name();
 
293
  if (name.empty())
 
294
    name = _connection->hostIdentifier();
 
295
  if (!name.empty())
 
296
    load_workspace(sanitize_file_name(name));
 
297
  
 
298
  if (_sql_editors.empty())
 
299
      add_sql_editor();
 
300
  
 
301
  if (_side_bar)
 
302
  {
 
303
    // update the info box
 
304
    schema_row_selected();
 
305
  }
 
306
  
 
307
  // Gets the title for a NEW editor
 
308
  _title = get_title(true);
 
309
  title_changed();
 
310
}
 
311
 
 
312
void SqlEditorForm::title_changed()
 
313
{
 
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);
 
319
}
 
320
 
 
321
SqlEditorForm::~SqlEditorForm()
 
322
{
 
323
  NotificationCenter::get()->remove_observer(this);
 
324
 
 
325
  delete _autosave_lock;
 
326
  _autosave_lock = 0;
 
327
  delete _info_tabview;
 
328
  delete _side_bar;
 
329
 
 
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();
 
337
 
 
338
  delete _session_info;
 
339
  delete _object_info;
 
340
 
 
341
  delete _toolbar;
 
342
  delete _menu;
 
343
  reset();
 
344
 
 
345
  reset_keep_alive_thread();
 
346
 
 
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);
 
352
}
 
353
 
 
354
 
 
355
void SqlEditorForm::handle_notification(const std::string &name, void *sender, base::NotificationInfo &info)
 
356
{
 
357
  if (name == "GNMainFormChanged")
 
358
  {
 
359
    if (_side_palette)
 
360
      _side_palette->close_popover();
 
361
    if (_wbsql->get_wbui()->get_active_main_form() == this)
 
362
      update_menu_and_toolbar();
 
363
  }
 
364
  else if (name == "GNFormTitleDidChange")
 
365
  {
 
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"))
 
368
    {
 
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);
 
373
      
 
374
      if (temp_title != _title)
 
375
      {
 
376
        _title = temp_title;
 
377
        title_changed();
 
378
      }
 
379
    }
 
380
  }
 
381
}
 
382
 
 
383
 
 
384
void SqlEditorForm::reset_keep_alive_thread()
 
385
{
 
386
  GMutexLock keep_alive_thread_lock(_keep_alive_thread_mutex);
 
387
  if (_keep_alive_thread)
 
388
  {
 
389
    _keep_alive_thread->stop(true);
 
390
    _keep_alive_thread= NULL;
 
391
  }
 
392
}
 
393
 
 
394
grt::StringRef SqlEditorForm::do_disconnect(grt::GRT *grt)
 
395
{
 
396
  if (_usr_dbc_conn->ref.get())
 
397
  {
 
398
    sql::Driver *dbc_driver= _usr_dbc_conn->ref->getDriver();
 
399
 
 
400
    close_connection(_usr_dbc_conn);
 
401
    close_connection(_aux_dbc_conn);
 
402
    _aux_dbc_conn->ref.reset();
 
403
    _usr_dbc_conn->ref.reset();
 
404
    
 
405
    dbc_driver->threadEnd();
 
406
  }
 
407
  return grt::StringRef();
 
408
}
 
409
 
 
410
bool SqlEditorForm::close()
 
411
{
 
412
  if (_close_done)
 
413
    return true;
 
414
  _close_done = true;
 
415
  
 
416
  if (exec_sql_task && exec_sql_task->is_busy())
 
417
  {
 
418
    _grtm->replace_status_text("Cannot close SQL Editor while busy");
 
419
    return false;
 
420
  }
 
421
 
 
422
  bool res= bec::UIForm::close();
 
423
  if (res)
 
424
  {
 
425
    grt::ValueRef option(_grtm->get_app_option("workbench:SaveSQLWorkspaceOnClose"));
 
426
    if (option.is_valid() && *grt::IntegerRef::cast_from(option))
 
427
    {
 
428
      _grtm->replace_status_text("Saving workspace state...");
 
429
      if (_autosave_path.empty())
 
430
      {
 
431
        save_workspace(sanitize_file_name(_connection->name()), false);
 
432
        delete _autosave_lock;
 
433
      }
 
434
      else
 
435
      {
 
436
        auto_save();
 
437
        
 
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");
 
441
        int try_count = 0;
 
442
 
 
443
        // Rename our temporary workspace if one exists to make it a persistent one.
 
444
        if (base::file_exists(_autosave_path))
 
445
        {
 
446
          for (;;)
 
447
          {
 
448
            try
 
449
            {
 
450
              if (base::file_exists(new_name))
 
451
                base::remove_recursive(new_name);
 
452
              base::rename(_autosave_path, new_name);
 
453
            }
 
454
            catch (base::file_error &err)
 
455
            {
 
456
              std::string path(dirname(_autosave_path));
 
457
              do
 
458
              {
 
459
                ++try_count;
 
460
                new_name = make_path(path, sanitize_file_name(_connection->name()).append(strfmt("-%i.workspace", try_count)));
 
461
              } while (file_exists(new_name));
 
462
 
 
463
              if (err.code() == base::already_exists)
 
464
                continue;
 
465
              log_warning("Could not rename autosave directory: %s\n", 
 
466
                _autosave_path.c_str(), err.what());
 
467
            }
 
468
 
 
469
            break;
 
470
          }
 
471
        }
 
472
      }
 
473
      _autosave_lock = 0;
 
474
    }
 
475
    else
 
476
    {
 
477
      delete _autosave_lock;
 
478
      _autosave_lock = 0;
 
479
      if (!_autosave_path.empty())
 
480
        base_rmdir_recursively(_autosave_path.c_str());
 
481
    }
 
482
 
 
483
    _grtm->replace_status_text("Closing SQL Editor...");
 
484
    wbsql()->editor_will_close(this);
 
485
 
 
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");
 
494
  }
 
495
  return res;
 
496
}
 
497
 
 
498
 
 
499
std::string SqlEditorForm::get_form_context_name() const
 
500
{
 
501
  return WB_CONTEXT_QUERY;
 
502
}
 
503
 
 
504
 
 
505
bool SqlEditorForm::get_session_variable(sql::Dbc_connection_handler::Ref &dbc_conn, const std::string &name, std::string &value)
 
506
{
 
507
  if (dbc_conn && dbc_conn->ref.get())
 
508
  {
 
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);
 
512
    if (query.empty())
 
513
      return false;
 
514
    boost::scoped_ptr<sql::Statement> statement(dbc_conn->ref->createStatement());
 
515
    boost::scoped_ptr<sql::ResultSet> rs(statement->executeQuery(query));
 
516
    if (rs->next())
 
517
    {
 
518
      value= rs->getString(2);
 
519
      return true;
 
520
    }
 
521
  }
 
522
  return false;
 
523
}
 
524
 
 
525
 
 
526
void SqlEditorForm::cache_sql_mode()
 
527
{
 
528
  std::string sql_mode;
 
529
  if (get_session_variable(_usr_dbc_conn, "sql_mode", sql_mode))
 
530
  {
 
531
    if (sql_mode != _sql_mode)
 
532
    {
 
533
      _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);
 
537
    }
 
538
  }
 
539
}
 
540
 
 
541
 
 
542
int SqlEditorForm::run_sql_in_scratch_tab(const std::string &sql, bool reuse_if_possible, bool start_collapsed)
 
543
{
 
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);
 
547
  run_sql();
 
548
  sql_editor_dirty(_active_sql_editor_index, false);
 
549
  
 
550
  return _active_sql_editor_index;
 
551
}
 
552
 
 
553
 
 
554
void SqlEditorForm::reset()
 
555
{
 
556
  //_log->reset();
 
557
}
 
558
 
 
559
 
 
560
RowId SqlEditorForm::add_log_message(int msg_type, const std::string &msg, const std::string &context, const std::string &duration)
 
561
{
 
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;
 
568
}
 
569
 
 
570
 
 
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)
 
572
{
 
573
  if (log_message_index != (RowId)-1)
 
574
  {
 
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);
 
583
  }
 
584
}
 
585
 
 
586
 
 
587
void SqlEditorForm::refresh_log_messages(bool ignore_last_message_timestamp)
 
588
{
 
589
  if (_has_pending_log_messages)
 
590
  {
 
591
    bool is_refresh_needed= ignore_last_message_timestamp;
 
592
    if (!ignore_last_message_timestamp)
 
593
    {
 
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;
 
598
    }
 
599
    if (is_refresh_needed)
 
600
    {
 
601
      exec_sql_task->send_progress(0.f, std::string(), std::string());
 
602
      _has_pending_log_messages= false;
 
603
    }
 
604
  }
 
605
}
 
606
 
 
607
 
 
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)
 
609
{
 
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);
 
615
}
 
616
 
 
617
std::string SqlEditorForm::get_text_for_actions(const std::list<int> &indexes, bool query_only)
 
618
{
 
619
  std::string sql;
 
620
  
 
621
  for (std::list<int>::const_iterator end = indexes.end(), it = indexes.begin(); it != end; ++it)
 
622
  {
 
623
    std::string s;
 
624
    
 
625
    if (!sql.empty())
 
626
      sql.append("\n");
 
627
    
 
628
    if (query_only)
 
629
    {
 
630
      _log->get_field(*it, 3, s);
 
631
      sql.append(s).append("\n");
 
632
    }
 
633
    else
 
634
    {
 
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");
 
643
    }
 
644
  }
 
645
  return sql;
 
646
}
 
647
 
 
648
int SqlEditorForm::recordset_count(int editor)
 
649
{
 
650
  if (editor >= 0 && editor < (int)_sql_editors.size())
 
651
    return sql_editor_recordsets(editor)->size();
 
652
  return 0;
 
653
}
 
654
 
 
655
 
 
656
Recordset::Ref SqlEditorForm::recordset(int editor, int new_index)
 
657
{
 
658
  if (editor >= 0 && editor < (int)_sql_editors.size())
 
659
    return sql_editor_recordsets(editor)->at(new_index);
 
660
  return Recordset::Ref();
 
661
}
 
662
 
 
663
 
 
664
void SqlEditorForm::init_connection(sql::Connection* dbc_conn_ref, const db_mgmt_ConnectionRef& connectionProperties, sql::Dbc_connection_handler::Ref& dbc_conn)
 
665
{
 
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();
 
669
 
 
670
  // connection startup script
 
671
  {
 
672
    std::list<std::string> sql_script;
 
673
    {
 
674
      sql_specifics->get_connection_startup_script(sql_script);
 
675
      bool use_ansi_quotes= (connectionProperties->parameterValues().get_int("useAnsiQuotes", 0) != 0);
 
676
      if (use_ansi_quotes)
 
677
      {
 
678
        std::string sql= sql_specifics->setting_ansi_quotes();
 
679
        if (!sql.empty())
 
680
          sql_script.push_back(sql);
 
681
      }
 
682
    }
 
683
    
 
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");
 
687
    
 
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);
 
691
  }
 
692
 
 
693
  // remember connection id
 
694
  {
 
695
    std::string query_connection_id= sql_specifics->query_connection_id();
 
696
    if (!query_connection_id.empty())
 
697
    {
 
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());
 
701
      rs->next();
 
702
      dbc_conn->id= rs->getInt(1);
 
703
    }
 
704
  }
 
705
}
 
706
 
 
707
 
 
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)
 
710
{
 
711
  dbc_conn->is_stop_query_requested= false;
 
712
 
 
713
  sql::DriverManager *dbc_drv_man= sql::DriverManager::getDriverManager();
 
714
 
 
715
  db_mgmt_ConnectionRef temp_connection = db_mgmt_ConnectionRef::cast_from(CopyContext(db_mgmt_conn.get_grt()).copy(db_mgmt_conn));
 
716
 
 
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));
 
720
 
 
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"));
 
727
  
 
728
  if (!authset)
 
729
    dbc_conn->ref= dbc_drv_man->getConnection(temp_connection, 
 
730
                                              boost::bind(&SqlEditorForm::init_connection, this, _1, _2, dbc_conn));
 
731
  else
 
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)
 
736
  {
 
737
    throw std::runtime_error("MySQL Server version is older than 5.0, which is not supported");
 
738
  }
 
739
 
 
740
  if (dbc_conn->active_schema.empty())
 
741
  {
 
742
    std::string last_default = temp_connection->parameterValues().get_string("DbSqlEditor:LastDefaultSchema");
 
743
    try
 
744
    {
 
745
      if (!last_default.empty())
 
746
        dbc_conn->ref->setSchema(last_default);
 
747
    }
 
748
    catch (std::exception &exc)
 
749
    {
 
750
      log_error("Can't restore LastDefaultSchema (%s): %s", last_default.c_str(), exc.what());
 
751
      temp_connection->parameterValues().gset("DbSqlEditor:LastDefaultSchema", "");
 
752
    }
 
753
 
 
754
    dbc_conn->active_schema= dbc_conn->ref->getSchema();
 
755
  }
 
756
  else
 
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();
 
760
}
 
761
 
 
762
 
 
763
void SqlEditorForm::connect()
 
764
{
 
765
  sql::AuthenticationSet authset;
 
766
 
 
767
  for (;;)
 
768
  {
 
769
    // if an auth error happens in the worker thread, this ptr will be set
 
770
    sql::AuthenticationError *auth_error_ptr = 0;
 
771
    
 
772
    // connection must happen in the worker thread
 
773
    try
 
774
    {
 
775
      exec_sql_task->exec(true, boost::bind(&SqlEditorForm::do_connect, this, _1, authset, boost::ref(auth_error_ptr)));
 
776
    }
 
777
    catch (grt::grt_runtime_error)
 
778
    {
 
779
      if (!auth_error_ptr)
 
780
        throw;
 
781
      if (auth_error_ptr->authentication() && !auth_error_ptr->authentication()->is_valid())
 
782
        authset.insert(auth_error_ptr->authentication());
 
783
      else
 
784
        throw;
 
785
      delete auth_error_ptr;
 
786
      
 
787
      for (sql::AuthenticationSet::iterator auth = authset.begin(); auth != authset.end(); ++auth)
 
788
      {
 
789
        if (!(*auth)->is_valid())
 
790
        {
 
791
          std::string pwd = sql::DriverManager::getDriverManager()->requestPassword((*auth)->connectionProperties(), true);
 
792
          (*auth)->set_password(pwd.c_str());
 
793
          break;
 
794
        }
 
795
      }
 
796
      continue;
 
797
    }
 
798
    break;
 
799
  }
 
800
}
 
801
 
 
802
//--------------------------------------------------------------------------------------------------
 
803
 
 
804
/**
 
805
 * Little helper to create a single html line used for info output.
 
806
 */
 
807
std::string create_html_line(const std::string& name, const std::string& value)
 
808
{
 
809
  return "<div style=\"padding-left: 15px\"><span style=\"color: #717171\">" + name + "</span> <i>" +
 
810
    value + "</i></div>";
 
811
}
 
812
 
 
813
//--------------------------------------------------------------------------------------------------
 
814
 
 
815
grt::StringRef SqlEditorForm::do_connect(grt::GRT *grt, sql::AuthenticationSet &authset, sql::AuthenticationError *&autherr_ptr)
 
816
{
 
817
  try
 
818
  {
 
819
    GMutexLock aux_dbc_conn_mutex(_aux_dbc_conn_mutex);
 
820
    GMutexLock usr_dbc_conn_mutex(_usr_dbc_conn_mutex);
 
821
 
 
822
    reset();
 
823
 
 
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);
 
828
 
 
829
    cache_sql_mode();
 
830
 
 
831
    // connection info
 
832
    try
 
833
    {
 
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");
 
842
  
 
843
      // Connection:
 
844
      _connection_info= "<div style=\"color=#3b3b3b; font-weight:bold\">Connection:</div>";
 
845
      _connection_info.append(create_html_line("Name: ", _connection->name()));
 
846
      // Host:
 
847
      if (_connection->driver()->name() == "MysqlNativeSocket")
 
848
      {
 
849
#ifdef _WIN32
 
850
        std::string name = _connection->parameterValues().get_string("socket", "");
 
851
        if (name.empty())
 
852
          name = "pipe";
 
853
#else
 
854
        std::string name = _connection->parameterValues().get_string("socket", "");
 
855
        if (name.empty())
 
856
          name = "UNIX socket";
 
857
#endif
 
858
        _connection_info.append(create_html_line("Host:", "localhost (" + name + ")"));
 
859
      }
 
860
      else
 
861
      {
 
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"))));
 
864
      }
 
865
 
 
866
      // Server:
 
867
      _connection_info.append(create_html_line("Server:", _connection_details["dbmsProductName"]));
 
868
      _connection_info.append(create_html_line("Version:",  _connection_details["dbmsProductVersion"]));
 
869
      // User:
 
870
      _connection_info.append(create_html_line("Login User:", _connection->parameterValues().get_string("userName")));
 
871
      
 
872
      // check the actual user we're logged in as
 
873
      if (_usr_dbc_conn && _usr_dbc_conn->ref.get())
 
874
      {
 
875
        boost::scoped_ptr<sql::Statement> statement(_usr_dbc_conn->ref->createStatement());
 
876
        boost::scoped_ptr<sql::ResultSet> rs(statement->executeQuery("SELECT current_user()"));
 
877
        if (rs->next())
 
878
          _connection_info.append(create_html_line("Current User:", rs->getString(1)));
 
879
      }
 
880
    }
 
881
    CATCH_ANY_EXCEPTION_AND_DISPATCH(_("Get connection information"));
 
882
  }
 
883
  catch (sql::AuthenticationError &exc)
 
884
  {
 
885
    autherr_ptr = new sql::AuthenticationError(exc);
 
886
    throw;
 
887
  }
 
888
 
 
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();
 
893
 
 
894
  workbench_physical_ModelRef model(_grtm->get_grt());
 
895
  rdbms->owner(model);
 
896
  model->rdbms(rdbms);
 
897
  db_CatalogRef catalog= _grtm->get_grt()->create_object<db_Catalog>(database_package + ".Catalog");
 
898
  {
 
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());
 
904
 
 
905
    // setup list of synonyms
 
906
    grt::Module *module= _grtm->get_grt()->get_module("DbMySQL");
 
907
    if (module)
 
908
    {
 
909
      grt::BaseListRef args(_grtm->get_grt());
 
910
      args.ginsert(rdbms);
 
911
      grt::ListRef<db_UserDatatype> user_types(grt::ListRef<db_UserDatatype>::cast_from(module->call_function("getDefaultUserDatatypes", args)));
 
912
      
 
913
      if (user_types.is_valid())
 
914
      {
 
915
        GRTLIST_FOREACH(db_UserDatatype, user_types, ut)
 
916
        {
 
917
          (*ut)->owner(catalog);
 
918
          catalog->userDatatypes().insert(*ut);
 
919
        }
 
920
      }
 
921
    }    
 
922
    
 
923
    model->catalog(catalog);
 
924
  }
 
925
   
 
926
  return grt::StringRef();
 
927
}
 
928
 
 
929
 
 
930
bool SqlEditorForm::connected() const
 
931
{
 
932
  return (_usr_dbc_conn && _usr_dbc_conn->ref.get_ptr() && !_usr_dbc_conn->ref->isClosed());
 
933
}
 
934
 
 
935
 
 
936
GMutexLock SqlEditorForm::ensure_valid_aux_connection()
 
937
{
 
938
  return ensure_valid_dbc_connection(_aux_dbc_conn, _aux_dbc_conn_mutex);
 
939
}
 
940
 
 
941
 
 
942
GMutexLock SqlEditorForm::ensure_valid_usr_connection()
 
943
{
 
944
  return ensure_valid_dbc_connection(_usr_dbc_conn, _usr_dbc_conn_mutex);
 
945
}
 
946
 
 
947
 
 
948
void SqlEditorForm::close_connection(sql::Dbc_connection_handler::Ref &dbc_conn)
 
949
{
 
950
  if (dbc_conn && dbc_conn->ref.get_ptr())
 
951
  {
 
952
    try
 
953
    {
 
954
      dbc_conn->ref->close();
 
955
    }
 
956
    catch (sql::SQLException &)
 
957
    {
 
958
      // ignore if the connection is already closed
 
959
    }
 
960
  }
 
961
}
 
962
 
 
963
 
 
964
GMutexLock SqlEditorForm::ensure_valid_dbc_connection(sql::Dbc_connection_handler::Ref &dbc_conn, GMutex *dbc_conn_mutex)
 
965
{
 
966
  GMutexLock mutex_lock(dbc_conn_mutex);
 
967
  bool valid= false;
 
968
  if (dbc_conn && dbc_conn->ref.get_ptr())
 
969
  {
 
970
    if (dbc_conn->ref->isClosed())
 
971
    {
 
972
      if (dbc_conn->autocommit_mode)
 
973
      {
 
974
        sql::AuthenticationSet authset;
 
975
        create_connection(dbc_conn, _connection, 0, dbc_conn->autocommit_mode);
 
976
        if (!dbc_conn->ref->isClosed())
 
977
          valid= true;
 
978
      }
 
979
    }
 
980
    else
 
981
      valid= true;
 
982
  }
 
983
  if (!valid)
 
984
    throw std::runtime_error("DBMS connection is not available");
 
985
 
 
986
  return mutex_lock;
 
987
}
 
988
 
 
989
 
 
990
bool SqlEditorForm::auto_commit()
 
991
{
 
992
  if (_usr_dbc_conn)
 
993
    return _usr_dbc_conn->autocommit_mode;
 
994
  return false;
 
995
}
 
996
 
 
997
 
 
998
void SqlEditorForm::auto_commit(bool value)
 
999
{
 
1000
  if (!_usr_dbc_conn)
 
1001
    return;
 
1002
  {
 
1003
    const char *STATEMENT= value ? "AUTOCOMMIT=1" : "AUTOCOMMIT=0";
 
1004
    try
 
1005
    {
 
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();
 
1009
    }
 
1010
    CATCH_ANY_EXCEPTION_AND_DISPATCH(STATEMENT)
 
1011
  }
 
1012
  update_menu_and_toolbar();
 
1013
}
 
1014
 
 
1015
 
 
1016
void SqlEditorForm::toggle_autocommit()
 
1017
{
 
1018
  auto_commit(!auto_commit());
 
1019
  update_menu_and_toolbar();
 
1020
}
 
1021
 
 
1022
 
 
1023
void SqlEditorForm::cancel_query()
 
1024
{
 
1025
  std::string query_kill_query;
 
1026
  {
 
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);
 
1031
  }
 
1032
  if (query_kill_query.empty())
 
1033
    return;
 
1034
 
 
1035
  const char *STATEMENT= "INTERRUPT";
 
1036
  RowId log_message_index= add_log_message(DbSqlEditorLog::BusyMsg, _("Running..."), STATEMENT, "");
 
1037
  Timer timer(false);
 
1038
 
 
1039
  try
 
1040
  {
 
1041
    {
 
1042
      GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
 
1043
      std::auto_ptr<sql::Statement> stmt(_aux_dbc_conn->ref->createStatement());
 
1044
      {
 
1045
        ScopeExitTrigger schedule_timer_stop(boost::bind(&Timer::stop, &timer));
 
1046
        timer.run();
 
1047
        stmt->execute(query_kill_query);
 
1048
        
 
1049
        // this can potentially cause threading issues, since connector driver isn't thread-safe
 
1050
        //close_connection(_usr_dbc_conn);
 
1051
 
 
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();
 
1055
      }
 
1056
    }
 
1057
 
 
1058
    if (_usr_dbc_conn->is_stop_query_requested)
 
1059
    {
 
1060
      _grtm->replace_status_text("Query Cancelled");
 
1061
      set_log_message(log_message_index, DbSqlEditorLog::NoteMsg, _("OK - Query cancelled"), STATEMENT, timer.duration_formatted());
 
1062
    }
 
1063
    else
 
1064
      set_log_message(log_message_index, DbSqlEditorLog::NoteMsg, _("OK - Query already completed"), STATEMENT, timer.duration_formatted());
 
1065
 
 
1066
    // reconnect but only if in autocommit mode
 
1067
    if (_usr_dbc_conn->autocommit_mode)
 
1068
    {
 
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),
 
1072
        false,
 
1073
        true);
 
1074
    }
 
1075
  }
 
1076
  CATCH_SQL_EXCEPTION_AND_DISPATCH(STATEMENT, log_message_index, "")
 
1077
}
 
1078
 
 
1079
 
 
1080
void SqlEditorForm::commit()
 
1081
{
 
1082
  exec_sql_retaining_editor_contents("COMMIT", Sql_editor::Ref(), false);
 
1083
}
 
1084
 
 
1085
 
 
1086
void SqlEditorForm::rollback()
 
1087
{
 
1088
  exec_sql_retaining_editor_contents("ROLLBACK", Sql_editor::Ref(), false);
 
1089
}
 
1090
 
 
1091
 
 
1092
void SqlEditorForm::explain_sql()
 
1093
{
 
1094
  int start, end;
 
1095
  Sql_editor::Ref sql_editor_= active_sql_editor();
 
1096
  if (sql_editor_)
 
1097
  {
 
1098
    sql_editor_->selected_range(start, end);
 
1099
    std::string sql= sql_editor_->sql();
 
1100
    if (start != end)
 
1101
      sql= sql.substr(start, end-start);
 
1102
 
 
1103
    do_explain_sql(sql);
 
1104
  }
 
1105
}
 
1106
 
 
1107
 
 
1108
void SqlEditorForm::explain_current_statement()
 
1109
{
 
1110
  Sql_editor::Ref sql_editor_= active_sql_editor();
 
1111
  if (sql_editor_)
 
1112
    do_explain_sql(sql_editor_->current_statement());
 
1113
}
 
1114
 
 
1115
 
 
1116
void SqlEditorForm::do_explain_sql(const std::string &sql)
 
1117
{
 
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";
 
1126
 
 
1127
  exec_sql_retaining_editor_contents(sql_script, active_sql_editor(), false);
 
1128
}
 
1129
 
 
1130
 
 
1131
void SqlEditorForm::exec_sql_retaining_editor_contents(const std::string &sql_script, Sql_editor::Ref editor, bool sync, bool dont_add_limit_clause)
 
1132
{
 
1133
  auto_save();
 
1134
  
 
1135
  RecordsetsRef recordsets;
 
1136
  int i = sql_editor_index(editor);
 
1137
  if (i >= 0)
 
1138
    recordsets = sql_editor_recordsets(i);
 
1139
  else
 
1140
    recordsets = RecordsetsRef(new Recordsets());  
 
1141
  
 
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)),
 
1146
               recordsets));
 
1147
}
 
1148
 
 
1149
 
 
1150
 
 
1151
void SqlEditorForm::run_sql()
 
1152
{
 
1153
  do_partial_ui_refresh(SqlEditorForm::RunCurrentScript);
 
1154
}
 
1155
 
 
1156
 
 
1157
 
 
1158
RecordsetsRef SqlEditorForm::exec_sql_returning_results(const std::string &sql_script, bool dont_add_limit_clause)
 
1159
{
 
1160
  RecordsetsRef rsets(new Recordsets());
 
1161
  std::string sql(sql_script);
 
1162
  
 
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),
 
1165
              rsets);
 
1166
  
 
1167
  return rsets;
 
1168
}
 
1169
 
 
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)
 
1171
{
 
1172
  ExecFlags flags = (ExecFlags)0;
 
1173
  
 
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);
 
1180
  auto_save();
 
1181
  
 
1182
  RecordsetsRef recordsets;
 
1183
 
 
1184
  int i = sql_editor_index(editor);
 
1185
  if (i >= 0)
 
1186
    recordsets = sql_editor_recordsets(i);
 
1187
  else
 
1188
    recordsets = RecordsetsRef(new Recordsets());
 
1189
 
 
1190
  exec_sql_task->exec(sync,
 
1191
    boost::bind(&SqlEditorForm::do_exec_sql, this, _1,
 
1192
               weak_ptr_from(this), sql, editor, 
 
1193
               flags,
 
1194
               recordsets));
 
1195
}
 
1196
 
 
1197
struct GuardBoolFlag
 
1198
{
 
1199
  bool *flag;
 
1200
  GuardBoolFlag(bool *ptr) : flag(ptr) { if (flag) *flag = true; }
 
1201
  ~GuardBoolFlag() { if (flag) *flag = false; }
 
1202
};
 
1203
 
 
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)
 
1205
{
 
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;
 
1209
 
 
1210
  int max_query_size_to_log = _options.get_int("DbSqlEditor:MaxQuerySizeToHistory", 0);
 
1211
  
 
1212
  _grtm->replace_status_text(_("Executing Query..."));
 
1213
 
 
1214
  RETVAL_IF_FAIL_TO_RETAIN_WEAK_PTR (SqlEditorForm, self_ptr, self, grt::StringRef(""))
 
1215
 
 
1216
  // add_log_message() will increment this variable on errors or warnings
 
1217
  _exec_sql_error_count = 0;
 
1218
 
 
1219
  GMutex *result_list_mutex = NULL;
 
1220
  sql::Driver *dbc_driver= NULL;
 
1221
  std::string statement;
 
1222
  int editor_index = -1;
 
1223
  try
 
1224
  {/*
 
1225
    if (!retaining)
 
1226
    {
 
1227
      if (!_recordsets_are_pinned_by_default)
 
1228
      {
 
1229
        GMutexLock recordsets_mutex(_recordsets_mutex);
 
1230
        Recordsets recordsets;
 
1231
        recordsets.reserve(result_list->size());
 
1232
        BOOST_FOREACH (Recordset::Ref rs, *result_list)
 
1233
        {
 
1234
          if (rs->pinned() || !rs->can_close(false))
 
1235
            recordsets.push_back(rs);
 
1236
          else
 
1237
            recordset_list_changed.emit(rs, false);
 
1238
        }
 
1239
        result_list->swap(recordsets);        
 
1240
      }
 
1241
    }*/
 
1242
    
 
1243
    int default_seq = 0;
 
1244
    int *rs_sequence = &default_seq;
 
1245
    bool *busy_flag = 0;
 
1246
 
 
1247
    if (editor && !retaining)
 
1248
    {
 
1249
      Sql_editor_info::Ref info;
 
1250
      editor_index = 0;
 
1251
      for (Sql_editors::iterator info_ = _sql_editors.begin(); info_ != _sql_editors.end(); ++info_, ++editor_index)
 
1252
      {
 
1253
        if ((*info_)->editor == editor)
 
1254
        {
 
1255
          info = *info_;
 
1256
          rs_sequence = &info->rs_sequence;
 
1257
          busy_flag = &info->busy;
 
1258
          break;
 
1259
        }
 
1260
      }
 
1261
      
 
1262
      GuardBoolFlag guard_busy_flag(busy_flag);
 
1263
 
 
1264
      // close all recordsets for the same editor
 
1265
      if (info)
 
1266
      {
 
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.");
 
1270
            
 
1271
        RecordsetsRef recordsets(new Recordsets());
 
1272
        recordsets->reserve(result_list->size());
 
1273
        int index = result_list->size();
 
1274
        while (--index >= 0)
 
1275
        {
 
1276
          if (!(*result_list)[index]->can_close(false))
 
1277
            recordsets->push_back((*result_list)[index]);
 
1278
          else
 
1279
          {
 
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())
 
1285
            {
 
1286
              if (info->active_recordset == *iter)
 
1287
                info->active_recordset.reset();
 
1288
              rsets->erase(iter);
 
1289
            }
 
1290
            if (editor_index >= 0)
 
1291
              recordset_list_changed(editor_index, current_recordset, false);
 
1292
          }
 
1293
        }
 
1294
        result_list->swap(*recordsets.get());
 
1295
        result_list_mutex = info->recordset_mutex;
 
1296
      }
 
1297
    }
 
1298
 
 
1299
    GMutexLock use_dbc_conn_mutex= ensure_valid_usr_connection();
 
1300
 
 
1301
    GuardBoolFlag guard_busy_flag(busy_flag);
 
1302
    
 
1303
    dbc_driver= _usr_dbc_conn->ref->getDriver();
 
1304
    dbc_driver->threadInit();
 
1305
 
 
1306
    bool is_running_query= true;
 
1307
    AutoSwap<bool> is_running_query_keeper(_is_running_query, is_running_query);
 
1308
    update_menu_and_toolbar();
 
1309
 
 
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));
 
1318
 
 
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();
 
1322
 
 
1323
    if (wrap_with_non_std_delimiter)
 
1324
      sql= sql_specifics->setting_non_std_sql_delimiter() + sql;
 
1325
 
 
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);
 
1330
 
 
1331
    if (!max_query_size_to_log || max_query_size_to_log >= (int)sql.size() )
 
1332
      _history->add_entry(statements);
 
1333
 
 
1334
    //std::list<Recordset::Ref> added_recordsets;
 
1335
    BOOST_FOREACH (statement, statements)
 
1336
    {
 
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);
 
1341
      {
 
1342
        statement= strip_text(statement, false, true);
 
1343
        if (statement.empty())
 
1344
          continue;
 
1345
 
 
1346
        Sql_syntax_check::Statement_type statement_type= sql_syntax_check->determine_statement_type(statement);
 
1347
        if (Sql_syntax_check::sql_empty == statement_type)
 
1348
          continue;
 
1349
 
 
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))
 
1354
        {
 
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();
 
1363
        }
 
1364
 
 
1365
        Recordset_cdbc_storage::Ref data_storage;
 
1366
 
 
1367
        // for select queries add limit clause if specified by global option
 
1368
        if (!is_multiple_statement && (Sql_syntax_check::sql_select == statement_type))
 
1369
        {
 
1370
          data_storage= Recordset_cdbc_storage::create(_grtm);
 
1371
          data_storage->rdbms(rdbms());
 
1372
          data_storage->dbms_conn(_usr_dbc_conn);
 
1373
          
 
1374
          std::string error = "";
 
1375
          
 
1376
          if(!table_name.empty() || is_editable_select(statement, schema_name, table_name, error))
 
1377
          {
 
1378
            data_storage->schema_name(schema_name);
 
1379
            data_storage->table_name(table_name);
 
1380
          }
 
1381
          else
 
1382
            data_storage->readonly_reason(error);
 
1383
 
 
1384
          data_storage->sql_query(statement);
 
1385
 
 
1386
          data_storage->additional_clauses(editable_query_tail);
 
1387
          {
 
1388
            bool limit_rows= !dont_add_limit_clause && (0 != _options.get_int("SqlEditor:LimitRows", 0));
 
1389
            data_storage->limit_rows(limit_rows);
 
1390
 
 
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);
 
1394
          }
 
1395
          statement= data_storage->decorated_sql_query();
 
1396
        }
 
1397
 
 
1398
        {
 
1399
          RowId log_message_index= add_log_message(DbSqlEditorLog::BusyMsg, _("Running..."), statement,
 
1400
            ((Sql_syntax_check::sql_select == statement_type) ? "? / ?" : "?"));
 
1401
 
 
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;
 
1408
 
 
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"));
 
1411
 
 
1412
          try
 
1413
          {
 
1414
            {
 
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);
 
1418
            }
 
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;
 
1424
          }
 
1425
          catch (sql::SQLException &e)
 
1426
          {
 
1427
            std::string err_msg;
 
1428
            // safe mode
 
1429
            switch (e.getErrorCode())
 
1430
            {
 
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());
 
1433
                break;
 
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());
 
1436
                break;
 
1437
              default:
 
1438
                err_msg= strfmt(_("Error Code: %i. %s"), e.getErrorCode(), e.what());
 
1439
                break;
 
1440
            }
 
1441
            set_log_message(log_message_index, DbSqlEditorLog::ErrorMsg, err_msg, statement, statement_exec_timer.duration_formatted());
 
1442
            statement_failed= true;
 
1443
          }
 
1444
          catch (std::exception &e)
 
1445
          {
 
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;
 
1449
          }
 
1450
          if (statement_failed)
 
1451
          {
 
1452
            if (_continue_on_error)
 
1453
              continue; // goto next statement
 
1454
            else
 
1455
              goto stop_processing_sql_script;
 
1456
          }
 
1457
 
 
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)
 
1463
          {
 
1464
            std::string message = strfmt(_("%lli row(s) affected"), updated_rows_count);
 
1465
            bool has_warning = false;
 
1466
            if (flags & Show_warnings)
 
1467
            {
 
1468
              std::string warnings_message;
 
1469
              const sql::SQLWarning *warnings= dbc_statement->getWarnings();
 
1470
              if (warnings)
 
1471
              {
 
1472
                int count= 0;
 
1473
                const sql::SQLWarning *w = warnings;
 
1474
                while (w) 
 
1475
                {
 
1476
                  warnings_message.append(strfmt("\n%i %s", w->getErrorCode(), w->getMessage().c_str()));
 
1477
                  count++; 
 
1478
                  w= w->getNextWarning();
 
1479
                }
 
1480
                message.append(strfmt(_(", %i warning(s):"), count));
 
1481
                has_warning = true;
 
1482
              }
 
1483
              if (!warnings_message.empty())
 
1484
                message.append(warnings_message);
 
1485
            }
 
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());            
 
1489
          }
 
1490
 
 
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)
 
1495
          {
 
1496
            for (int processed_substatements_count= 0; processed_substatements_count < multiple_statement_count; ++processed_substatements_count)
 
1497
            {
 
1498
              do
 
1499
              {
 
1500
                if (more_results)
 
1501
                {
 
1502
                  if (!reuse_log_msg && ((updated_rows_count >= 0) || (resultset_count)))
 
1503
                    log_message_index= add_log_message(DbSqlEditorLog::BusyMsg, _("Fetching..."), statement, "- / ?");
 
1504
                  else
 
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;
 
1508
                  {
 
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());
 
1512
                  }
 
1513
                  if (dbc_resultset)
 
1514
                  {
 
1515
                    if (!data_storage)
 
1516
                    {
 
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);
 
1524
                    }
 
1525
 
 
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));
 
1529
 
 
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()),
 
1536
                                       ++*rs_sequence));
 
1537
                    rs->generator_query(statement);
 
1538
                    rs->action_list().register_action("recall_query",
 
1539
                      boost::bind(&SqlEditorForm::recall_recordset_query, this, Recordset::Ptr(rs)));
 
1540
 
 
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));
 
1544
 
 
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));
 
1549
                    rs->reset(true);
 
1550
                    if (data_storage->valid()) // query statement
 
1551
                    {
 
1552
                      {
 
1553
                        GMutexLock recordsets_mutex(result_list_mutex);
 
1554
                        result_list->push_back(rs);
 
1555
                      }
 
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);
 
1565
                    }
 
1566
                    //! else failed to fetch data
 
1567
                    //added_recordsets.push_back(rs);
 
1568
                    ++resultset_count;
 
1569
                  }
 
1570
                  else
 
1571
                  {
 
1572
                    reuse_log_msg= true;
 
1573
                  }
 
1574
                  data_storage.reset();
 
1575
                }
 
1576
              }
 
1577
              while ((more_results= dbc_statement->getMoreResults()));
 
1578
            }
 
1579
          }
 
1580
          
 
1581
          if ((updated_rows_count < 0) && !(resultset_count))
 
1582
          {
 
1583
            set_log_message(log_message_index, DbSqlEditorLog::OKMsg, _("OK"), statement, statement_exec_timer.duration_formatted());
 
1584
          }
 
1585
        }
 
1586
      }
 
1587
    } // BOOST_FOREACH (statement, statements)
 
1588
 
 
1589
    _grtm->replace_status_text(_("Query Completed"));
 
1590
 
 
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)
 
1595
      cache_sql_mode();
 
1596
  }
 
1597
  CATCH_ANY_EXCEPTION_AND_DISPATCH(statement)
 
1598
 
 
1599
  if (dbc_driver)
 
1600
    dbc_driver->threadEnd();
 
1601
 
 
1602
  update_menu_and_toolbar();
 
1603
  
 
1604
  _usr_dbc_conn->is_stop_query_requested = false;
 
1605
 
 
1606
  return grt::StringRef("");
 
1607
}
 
1608
 
 
1609
 
 
1610
bool SqlEditorForm::is_running_query()
 
1611
{
 
1612
  return _is_running_query;
 
1613
}
 
1614
 
 
1615
 
 
1616
void SqlEditorForm::continue_on_error(bool val)
 
1617
{
 
1618
  if (_continue_on_error == val)
 
1619
    return;
 
1620
 
 
1621
  _continue_on_error= val;
 
1622
  _options.set("DbSqlEditor:ContinueOnError", grt::IntegerRef((int)_continue_on_error));
 
1623
  
 
1624
  if (_menu)
 
1625
    _menu->set_item_checked("query.stopOnError", !continue_on_error());
 
1626
  set_editor_tool_items_checked("query.stopOnError", !continue_on_error());
 
1627
}
 
1628
 
 
1629
 
 
1630
void SqlEditorForm::send_message_keep_alive()
 
1631
{
 
1632
  try
 
1633
  {
 
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();
 
1638
  }
 
1639
  catch (const std::exception &)
 
1640
  {
 
1641
  }
 
1642
}
 
1643
 
 
1644
 
 
1645
Recordset::Ref SqlEditorForm::active_recordset(int editor)
 
1646
{
 
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();
 
1651
}
 
1652
 
 
1653
void SqlEditorForm::active_recordset(int editor, Recordset::Ref rset)
 
1654
{
 
1655
  GMutexLock lock(_sql_editors_mutex);
 
1656
  if (editor >= 0 && editor < (int)_sql_editors.size())
 
1657
    _sql_editors[editor]->active_recordset = rset;
 
1658
 
 
1659
  if (_menu)
 
1660
  {
 
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);
 
1664
  }
 
1665
}
 
1666
 
 
1667
bool SqlEditorForm::recordset_reorder(int editor, Recordset::Ref value, int new_index)
 
1668
{
 
1669
  GMutexTryLock lock(_sql_editors_mutex);
 
1670
  if (lock.locked())
 
1671
  {
 
1672
    if (editor >= 0 && editor < (int)_sql_editors.size())
 
1673
    {
 
1674
      GMutexTryLock rlock(_sql_editors[editor]->recordset_mutex);
 
1675
      if (rlock.locked())
 
1676
      {
 
1677
        RecordsetsRef recordsets(sql_editor_recordsets(editor));
 
1678
 
 
1679
        Recordsets::iterator iter = std::find(recordsets->begin(), recordsets->end(), value);
 
1680
        if (iter != recordsets->end())
 
1681
        {
 
1682
          if (new_index >= (int)recordsets->size()-1)
 
1683
            new_index= -1;
 
1684
          recordsets->erase(iter);
 
1685
          if (new_index < 0)
 
1686
            recordsets->push_back(value);
 
1687
          else
 
1688
            recordsets->insert(recordsets->begin() + new_index, value);
 
1689
          return true;
 
1690
        }
 
1691
      }
 
1692
    }
 
1693
  }
 
1694
  return false;
 
1695
}
 
1696
 
 
1697
 
 
1698
void SqlEditorForm::on_close_recordset(Recordset::Ptr rs_ptr)
 
1699
{
 
1700
  int editor = 0;
 
1701
  RETURN_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs)
 
1702
  {
 
1703
    {
 
1704
      GMutexLock ed_lock(_sql_editors_mutex);
 
1705
      editor = sql_editor_index_for_recordset(rs_ref->key());
 
1706
      if (editor >= 0)
 
1707
      {
 
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();
 
1713
      }
 
1714
    }
 
1715
    recordset_list_changed(editor, rs_ref, false);
 
1716
    //delme close_recordset_ui.emit(rs->key());    
 
1717
  }
 
1718
}
 
1719
 
 
1720
void SqlEditorForm::recall_recordset_query(Recordset::Ptr rs_ptr)
 
1721
{
 
1722
  RETURN_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs)
 
1723
  {
 
1724
    std::string query = rs->generator_query();
 
1725
    
 
1726
    new_sql_scratch_area();
 
1727
    set_sql_editor_text(query);
 
1728
  }
 
1729
}
 
1730
 
 
1731
void SqlEditorForm::apply_changes_to_recordset(Recordset::Ptr rs_ptr)
 
1732
{
 
1733
  RETURN_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs)
 
1734
 
 
1735
  try
 
1736
  {
 
1737
    GMutexLock usr_dbc_conn_mutex= ensure_valid_usr_connection();
 
1738
 
 
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;
 
1743
    int res= -2;
 
1744
    if (!auto_commit)
 
1745
    {
 
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"),
 
1751
        _("Cancel"),
 
1752
        _("Apply"));
 
1753
 
 
1754
      if (res == mforms::ResultOk)
 
1755
      {
 
1756
        _usr_dbc_conn->ref->commit();
 
1757
      }
 
1758
    }
 
1759
    else
 
1760
    {
 
1761
      autocommit_mode_keeper.slot= boost::bind(
 
1762
        &sql::Connection::setAutoCommit, _usr_dbc_conn->ref.get(), 
 
1763
        auto_commit);
 
1764
      _usr_dbc_conn->ref->setAutoCommit(false);
 
1765
    }
 
1766
 
 
1767
    if (res != mforms::ResultCancel) // only if not canceled
 
1768
    {
 
1769
      bool is_data_changes_commit_wizard_enabled= (0 != _options.get_int("DbSqlEditor:IsDataChangesCommitWizardEnabled", 1));
 
1770
      if (is_data_changes_commit_wizard_enabled)
 
1771
      {
 
1772
        /*bool are_changes_committed= */run_data_changes_commit_wizard(rs_ptr);
 
1773
      }
 
1774
      else
 
1775
      {
 
1776
        rs->apply_changes_();
 
1777
      }
 
1778
    }
 
1779
  }
 
1780
  CATCH_ANY_EXCEPTION_AND_DISPATCH(_("Apply changes to recordset"))
 
1781
}
 
1782
 
 
1783
 
 
1784
bool SqlEditorForm::run_data_changes_commit_wizard(Recordset::Ptr rs_ptr)
 
1785
{
 
1786
  RETVAL_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs, false)
 
1787
 
 
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();
 
1791
 
 
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());
 
1796
  if (!sql_storage)
 
1797
    return false;
 
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);
 
1802
 
 
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);
 
1809
  wizard.run_modal();
 
1810
 
 
1811
  return !wizard.has_errors();
 
1812
}
 
1813
 
 
1814
 
 
1815
void SqlEditorForm::apply_data_changes_commit(std::string &sql_script_text, Recordset::Ptr rs_ptr)
 
1816
{
 
1817
  RETURN_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs);
 
1818
 
 
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();
 
1821
 
 
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());
 
1824
  if (!sql_storage)
 
1825
    return;
 
1826
 
 
1827
  int max_query_size_to_log = _options.get_int("DbSqlEditor:MaxQuerySizeToHistory", 0);
 
1828
 
 
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);
 
1833
 
 
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));
 
1837
 
 
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));
 
1840
 
 
1841
  if (!max_query_size_to_log || max_query_size_to_log >= (int)sql_script_text.size() )
 
1842
    _history->add_entry(sql_script.statements);
 
1843
}
 
1844
 
 
1845
 
 
1846
std::string SqlEditorForm::active_schema() const
 
1847
{
 
1848
  return (_usr_dbc_conn) ? _usr_dbc_conn->active_schema : std::string();
 
1849
}
 
1850
 
 
1851
 
 
1852
int SqlEditorForm::active_schema_index() const
 
1853
{
 
1854
  return _schema_tree->get_index_of_schema(active_schema());
 
1855
}
 
1856
 
 
1857
 
 
1858
void SqlEditorForm::cache_active_schema_name()
 
1859
{
 
1860
  std::string schema=_usr_dbc_conn->ref->getSchema();
 
1861
  _usr_dbc_conn->active_schema= schema;
 
1862
  _aux_dbc_conn->active_schema= schema;
 
1863
 
 
1864
  _schema_tree->set_active_schema(schema);
 
1865
 
 
1866
  exec_sql_task->execute_in_main_thread(
 
1867
      boost::bind(&SqlEditorForm::update_editor_title_schema, this, schema),
 
1868
      false,
 
1869
      true);
 
1870
 
 
1871
  if (_side_bar)
 
1872
  {
 
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()));
 
1875
  }
 
1876
}
 
1877
 
 
1878
 
 
1879
void SqlEditorForm::active_schema(const std::string &value)
 
1880
{
 
1881
  try
 
1882
  {
 
1883
    if (value.empty())
 
1884
      return;
 
1885
 
 
1886
    std::string schema= active_schema();
 
1887
    if (value == schema)
 
1888
      return;
 
1889
 
 
1890
    {
 
1891
      GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
 
1892
      _aux_dbc_conn->ref->setSchema(value);
 
1893
      _aux_dbc_conn->active_schema= value;
 
1894
    }
 
1895
 
 
1896
    {
 
1897
      GMutexLock usr_dbc_conn_mutex= ensure_valid_usr_connection();
 
1898
      _usr_dbc_conn->ref->setSchema(value);
 
1899
      _usr_dbc_conn->active_schema= value;
 
1900
    }
 
1901
    
 
1902
    _schema_tree->set_active_schema(value);
 
1903
    if (_side_bar)
 
1904
    {
 
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());
 
1907
    }
 
1908
    // remember active schema
 
1909
    _connection->parameterValues().gset("DbSqlEditor:LastDefaultSchema", value);
 
1910
 
 
1911
    update_editor_title_schema(value);
 
1912
    
 
1913
    grt_manager()->replace_status_text(strfmt(_("Active schema changed to %s"), value.c_str()));
 
1914
  }
 
1915
  CATCH_ANY_EXCEPTION_AND_DISPATCH(_("Set active schema"))
 
1916
}
 
1917
 
 
1918
 
 
1919
wb::LiveSchemaTree *SqlEditorForm::get_base_schema_tree()
 
1920
{
 
1921
  return &_base_schema_tree;
 
1922
}
 
1923
 
 
1924
wb::LiveSchemaTree *SqlEditorForm::get_schema_tree()
 
1925
{
 
1926
  return _schema_tree;
 
1927
}
 
1928
 
 
1929
wb::LiveSchemaTree *SqlEditorForm::get_filtered_schema_tree()
 
1930
{
 
1931
  return &_filtered_schema_tree;
 
1932
}
 
1933
 
 
1934
db_mgmt_RdbmsRef SqlEditorForm::rdbms()
 
1935
{
 
1936
  if (_connection.is_valid())
 
1937
  {
 
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());
 
1941
  }
 
1942
  else
 
1943
    return db_mgmt_RdbmsRef::cast_from(_grtm->get_grt()->get("/wb/doc/physicalModels/0/rdbms"));
 
1944
}
 
1945
 
 
1946
 
 
1947
int SqlEditorForm::count_connection_editors(std::string conn_name)
 
1948
{
 
1949
  int count = 0;
 
1950
  boost::weak_ptr<SqlEditorForm> editor;
 
1951
  
 
1952
  std::list<boost::weak_ptr<SqlEditorForm> >::iterator index, end;
 
1953
  
 
1954
  end = _wbsql->get_open_editors()->end();
 
1955
  for(index = _wbsql->get_open_editors()->begin(); index != end; index++)
 
1956
  {
 
1957
    SqlEditorForm::Ref editor((*index).lock());
 
1958
    std::string editor_connection = editor->_connection->name();
 
1959
    if (editor_connection == conn_name)
 
1960
      count++;    
 
1961
  }
 
1962
  
 
1963
  return count;  
 
1964
}
 
1965
 
 
1966
std::string SqlEditorForm::caption() const
 
1967
{
 
1968
  return _title;
 
1969
}
 
1970
 
 
1971
std::string SqlEditorForm::get_title(bool new_editor)
 
1972
{
 
1973
  int count_limit = new_editor ? 0 : 1;
 
1974
  std::string caption= _("SQL Editor");
 
1975
  std::string editor_connection = _connection->name();
 
1976
  
 
1977
  if (*_connection->name().c_str())
 
1978
    caption+= strfmt(" (%s", _connection->name().c_str());
 
1979
  else
 
1980
  {
 
1981
    if (_connection->driver()->name() == "MysqlNativeSocket")
 
1982
      caption+= " (localhost";
 
1983
    else
 
1984
      caption+= strfmt(" (%s", truncate_text(editor_connection,21).c_str());
 
1985
  }
 
1986
 
 
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());
 
1997
 
 
1998
  caption+= ")";
 
1999
 
 
2000
  return caption;
 
2001
}
 
2002
 
 
2003
 
 
2004
bool SqlEditorForm::save_snippet(const std::string &text)
 
2005
{
 
2006
  if (text.empty()) return false;
 
2007
  
 
2008
  std::string name;
 
2009
  if (!wbsql()->get_wbui()->get_wb()->request_input(_("Enter a Name for the Snippet (optional):"), 0, name))
 
2010
    return false;
 
2011
 
 
2012
  DbSqlEditorSnippets::get_instance()->add_snippet(name, text);
 
2013
  
 
2014
  return true;
 
2015
}
 
2016
 
 
2017
//--------------------------------------------------------------------------------------------------
 
2018
 
 
2019
bool SqlEditorForm::can_close()
 
2020
{  
 
2021
  return can_close_(true);
 
2022
}
 
2023
 
 
2024
 
 
2025
bool SqlEditorForm::can_close_(bool interactive)
 
2026
{
 
2027
  bool check_editors = true;
 
2028
  
 
2029
  if (!bec::UIForm::can_close())
 
2030
    return false;
 
2031
 
 
2032
  if (_side_bar)
 
2033
    _options.gset("DbSqlEditor:SidebarCollapseState", _side_bar->get_collapse_states());
 
2034
  
 
2035
  {
 
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;
 
2041
  }
 
2042
 
 
2043
  if (interactive)
 
2044
  {
 
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++)
 
2047
    {
 
2048
      if (sql_editor_dirty(i) && check_editors)
 
2049
      {
 
2050
        std::string n= sql_editor_path(i);
 
2051
        if (!n.empty())
 
2052
          n= std::string(g_basename(n.c_str())).append(" - ").append(n);
 
2053
        else
 
2054
          n= "Unsaved SQL Query";
 
2055
        dialog.add_item("Script Buffers", n);
 
2056
      }
 
2057
 
 
2058
      RecordsetsRef rsets = sql_editor_recordsets(i);
 
2059
      BOOST_FOREACH (Recordset::Ref rs, *(rsets.get()))
 
2060
      {
 
2061
        if (!rs->can_close(false))
 
2062
          dialog.add_item("Resultset", rs->caption());
 
2063
      }
 
2064
    }
 
2065
    
 
2066
    bool review= false;
 
2067
    if (dialog.change_count() > 1)
 
2068
    {
 
2069
      switch (dialog.run()) 
 
2070
      {
 
2071
        case ConfirmSaveDialog::ReviewChanges:
 
2072
          review= true;
 
2073
          break;
 
2074
 
 
2075
        case ConfirmSaveDialog::DiscardChanges:
 
2076
          review= false;
 
2077
          break;
 
2078
 
 
2079
        case ConfirmSaveDialog::Cancel:
 
2080
          return false;
 
2081
      }
 
2082
    }
 
2083
    else if (dialog.change_count() == 1)
 
2084
      review= true;
 
2085
     
 
2086
    // review changes 1 by 1
 
2087
    if (review && check_editors)
 
2088
    {
 
2089
      for (int i = 0; i < sql_editor_count(); i++)
 
2090
      {
 
2091
        // sql_editor_wilL_close() checks for unsaved recordsets
 
2092
        if (!sql_editor_will_close(i))
 
2093
          return false;
 
2094
      }
 
2095
    }
 
2096
  }
 
2097
  else // !interactive
 
2098
  {
 
2099
    for (int i = 0; i < sql_editor_count(); i++)
 
2100
    {
 
2101
      if (check_editors && sql_editor_dirty(i))
 
2102
        return false;
 
2103
 
 
2104
      {
 
2105
        GMutexTryLock lock(_sql_editors[i]->recordset_mutex);
 
2106
        if (!lock.locked())
 
2107
          return false;
 
2108
 
 
2109
        RecordsetsRef rsets = sql_editor_recordsets(i);
 
2110
        BOOST_FOREACH (Recordset::Ref rs, *(rsets.get()))
 
2111
        {
 
2112
          if (!rs->can_close(false))
 
2113
            return false;
 
2114
        }
 
2115
      }
 
2116
    }
 
2117
  }
 
2118
 
 
2119
  return true;
 
2120
}
 
2121
 
 
2122
 
 
2123
 
 
2124
bec::MenuItemList SqlEditorForm::recordset_popup_menu_items(const std::vector<int> &rows, int column, Recordset::Ptr rs_ptr)
 
2125
{
 
2126
  RETVAL_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs, bec::MenuItemList())
 
2127
  if (rs)
 
2128
  {
 
2129
    bec::ArgumentPool argpool;
 
2130
    
 
2131
    if (!rows.empty())
 
2132
    {
 
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]));
 
2138
    }
 
2139
    argpool.add_simple_value("clickedColumn", grt::IntegerRef(column));
 
2140
 
 
2141
    _context_ui->get_wb()->update_plugin_arguments_pool(argpool);
 
2142
 
 
2143
    db_query_EditorRef editor(_wbsql->get_grt_editor_object(this));
 
2144
    
 
2145
    for (size_t c= editor->resultsets().count(), i= 0; i < c; i++)
 
2146
    {
 
2147
      db_query_ResultsetRef rset(editor->resultsets()[i]);
 
2148
      if (rset->get_data()->recordset.get() == rs)
 
2149
      {
 
2150
        // add rset to the pool, overriding the one added by update_args
 
2151
        argpool.add_entries_for_object("", rset, "db.query.Resultset");
 
2152
        break;
 
2153
      }
 
2154
    }
 
2155
 
 
2156
    std::list<std::string> groups;
 
2157
    groups.push_back("Menu/SQL/Resultset");
 
2158
 
 
2159
    return grt_manager()->get_plugin_context_menu_items(groups, argpool);    
 
2160
  }
 
2161
  
 
2162
  return bec::MenuItemList();
 
2163
}
 
2164
 
 
2165
 
 
2166
bool SqlEditorForm::call_recordset_popup_menu_item(const std::string &item, const std::vector<int> &rows,
 
2167
                                                   int column, Recordset::Ptr rs_ptr)
 
2168
{
 
2169
  RETVAL_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs, false)
 
2170
  
 
2171
  if (rs)
 
2172
  {
 
2173
    bec::ArgumentPool argpool;
 
2174
    
 
2175
    if (!rows.empty())
 
2176
    {
 
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]);
 
2182
    }
 
2183
    argpool["app.PluginInputDefinition:clickedColumn"]= grt::IntegerRef(column);
 
2184
    
 
2185
    _context_ui->get_wb()->update_plugin_arguments_pool(argpool);
 
2186
    
 
2187
    db_query_EditorRef editor(_wbsql->get_grt_editor_object(this));
 
2188
    
 
2189
    for (size_t c= editor->resultsets().count(), i= 0; i < c; i++)
 
2190
    {
 
2191
      db_query_ResultsetRef rset(editor->resultsets()[i]);
 
2192
      if (rset->get_data()->recordset.get() == rs)
 
2193
      {
 
2194
        // add rset to the pool, overriding the one added by update_args
 
2195
        argpool.add_entries_for_object("", rset, "db.query.Resultset");
 
2196
        break;
 
2197
      }
 
2198
    }
 
2199
    
 
2200
    return _context_ui->get_command_ui()->activate_command(item, argpool);
 
2201
  }
 
2202
  
 
2203
  return false;
 
2204
}
 
2205
 
 
2206
 
 
2207
//--------------------------------------------------------------------------------------------------
 
2208
 
 
2209
bool SqlEditorForm::is_editable_select(const std::string& statement, std::string &schema_name, std::string& table_name, std::string& error)
 
2210
{
 
2211
  SqlFacade::Ref sql_facade= SqlFacade::instance_for_rdbms(rdbms());
 
2212
  SqlFacade::Column_name_list column_names;
 
2213
  schema_name = "";
 
2214
  table_name = "";
 
2215
  int pk_count = 0;
 
2216
  bool missing_pk = false;
 
2217
  bool found_id = false;
 
2218
 
 
2219
  error = "";
 
2220
 
 
2221
  // Inserts the key columns if not already present included
 
2222
  if (sql_facade->parseSelectStatementForEdit(statement, schema_name, table_name, column_names))
 
2223
  {
 
2224
    bool fetch_table_data = true;
 
2225
 
 
2226
    if ( schema_name.length() == 0 )
 
2227
      schema_name = _usr_dbc_conn->active_schema;
 
2228
 
 
2229
    LiveSchemaTree::TableNode table;
 
2230
 
 
2231
    bec::NodeId table_node = _schema_tree->get_node_for_object(schema_name, LiveSchemaTree::Table, table_name);
 
2232
 
 
2233
    if (table_node.is_valid())
 
2234
    {
 
2235
      table = *dynamic_cast<LiveSchemaTree::TableNode *>(_schema_tree->get_object_node_by_index(table_node));
 
2236
      
 
2237
      // If the column info is not available it must be fetched
 
2238
      fetch_table_data = !table.columns_loaded;
 
2239
    }
 
2240
 
 
2241
    // If the table information has not been loaded
 
2242
    if (fetch_table_data)
 
2243
    {
 
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));
 
2247
    }
 
2248
 
 
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++)
 
2251
    {
 
2252
      if (table.columns[index].is_id)
 
2253
      {
 
2254
        // It could be ID and not PK
 
2255
        bool is_pk = table.columns[index].is_pk;
 
2256
 
 
2257
        std::string column_name = table.columns[index].name;
 
2258
        
 
2259
        // Counts the primary key columns
 
2260
        if (is_pk)
 
2261
          pk_count++;
 
2262
 
 
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 != "*")
 
2265
        {
 
2266
          bool found = false;
 
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);
 
2270
 
 
2271
          if (!found && is_pk)
 
2272
            missing_pk = true;
 
2273
 
 
2274
          if (found && !is_pk)
 
2275
            found_id = true;
 
2276
        }
 
2277
      }
 
2278
    }
 
2279
 
 
2280
    if (!found_id)
 
2281
    {
 
2282
      if(pk_count == 0)
 
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());
 
2284
      else if(missing_pk)
 
2285
        error = base::strfmt("The select statement is missing primary key or unique non nullable columns on table %s.", table_name.c_str());
 
2286
    }
 
2287
  }
 
2288
  else
 
2289
    error = "The statement is not editable.";
 
2290
 
 
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;
 
2295
}
 
2296
 
 
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)
 
2298
{
 
2299
  *table_node = *obj_node;
 
2300
}
 
2301
 
 
2302
void SqlEditorForm::update_editor_title_schema(const std::string& schema)
 
2303
{
 
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);
 
2307
 
 
2308
    if (temp_title != _title)      
 
2309
    {
 
2310
      _title = temp_title;
 
2311
      title_changed();
 
2312
    }
 
2313
}
 
 
b'\\ No newline at end of file'