/* 
 * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include "stdafx.h"

#include "wb_sql_editor_tree_controller.h"
#include "base/sqlstring.h"
#include "base/util_functions.h"
#include "base/string_utilities.h"
#include "base/boost_smart_ptr_helpers.h"
#include "base/log.h"

#include <boost/foreach.hpp>
#include <boost/signals2/connection.hpp>

#include "diff/diffchange.h"
#include "grtdb/diff_dbobjectmatch.h"
#include "db.mysql/src/module_db_mysql.h"
#include "sqlide/sql_script_run_wizard.h"

#include "grtdb/editor_table.h"
#include "grtsqlparser/sql_facade.h"
#include "workbench/wb_context_ui.h"
#include "sqlide/wb_live_schema_tree.h"

#include "mforms/grttreeview.h"
#include "mforms/menubar.h"
#include "mforms/textentry.h"

using namespace grt;
using namespace bec;
using namespace wb;
using namespace base;

using boost::signals2::scoped_connection;

DEFAULT_LOG_DOMAIN("SqlEditorSchemaTree");

static const char *SQL_EXCEPTION_MSG_FORMAT= _("Error Code: %i\n%s");
static const char *EXCEPTION_MSG_FORMAT= _("Error: %s");

#define CATCH_ANY_EXCEPTION_AND_DISPATCH(statement) \
catch (sql::SQLException &e)\
{\
add_log_message(DbSqlEditorLog::ErrorMsg, strfmt(SQL_EXCEPTION_MSG_FORMAT, e.getErrorCode(), e.what()), statement, "");\
}\
CATCH_EXCEPTION_AND_DISPATCH(statement)

#define CATCH_EXCEPTION_AND_DISPATCH(statement) \
catch (std::exception &e)\
{\
add_log_message(DbSqlEditorLog::ErrorMsg, strfmt(EXCEPTION_MSG_FORMAT, e.what()), statement, "");\
}

#define CATCH_ANY_EXCEPTION_AND_DISPATCH(statement) \
catch (sql::SQLException &e)\
{\
add_log_message(DbSqlEditorLog::ErrorMsg, strfmt(SQL_EXCEPTION_MSG_FORMAT, e.getErrorCode(), e.what()), statement, "");\
}\
CATCH_EXCEPTION_AND_DISPATCH(statement)

#define CATCH_ANY_EXCEPTION_AND_DISPATCH_TO_DEFAULT_LOG(statement) \
catch (sql::SQLException &e)\
{\
_grtm->get_grt()->send_error(strfmt(SQL_EXCEPTION_MSG_FORMAT, e.getErrorCode(), e.what()), statement);\
}\
catch (std::exception &e)\
{\
_grtm->get_grt()->send_error(strfmt(EXCEPTION_MSG_FORMAT, e.what()), statement);\
}


std::list<std::string> SqlEditorForm::fetch_schema_list()
{
  std::list<std::string> schemata_names;
  try
  {
    GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
    
    bool show_metadata_schemata= (0 != _options.get_int("DbSqlEditor:ShowMetadataSchemata", 0));
    
    std::auto_ptr<sql::ResultSet> rs(_aux_dbc_conn->ref->getMetaData()->getSchemata());
    while (rs->next())
    {
      std::string name= rs->getString(1);
      static std::map<std::string, bool> metadata_schemata_names;
      class MetadataSchemataNamesInitializer
      {
      public:
        MetadataSchemataNamesInitializer(std::map<std::string, bool> &metadata_schemata_names)
        {
          //! dbms-specific code
          metadata_schemata_names["information_schema"];
          metadata_schemata_names["performance_schema"];
          metadata_schemata_names["mysql"];
        }
      };
      static MetadataSchemataNamesInitializer metadata_schemata_initializer(metadata_schemata_names);
      
      if (show_metadata_schemata || (metadata_schemata_names.end() == metadata_schemata_names.find(name)))
        schemata_names.push_back(name);
    }
  }
  CATCH_ANY_EXCEPTION_AND_DISPATCH(_("Get schemata"))
  return schemata_names;
}



void SqlEditorForm::notify_of_fetched_schema_contents(
                                                      Ptr self_ptr,
                                                      bec::NodeId node)
{
  RETURN_IF_FAIL_TO_RETAIN_WEAK_PTR (SqlEditorForm, self_ptr, self)
  
  _schema_tree->tree_changed(0, node);
  //    _schema_tree.refresh_node_ui.emit(node);

  mforms::GRTTreeView *tree = _side_bar->get_schema_tree();
  for (int i = 0; i < 3; i++)
  {
    bec::NodeId child(node);
    child = child.append(i);
    if (_schema_tree->is_expanded(child))
      tree->set_expanded(child, true);
  }
}


bool SqlEditorForm::fetch_schema_contents(bec::NodeId node_id, 
                                          const std::string &schema_name, 
                                          bool include_column_data,
                                          const wb::LiveSchemaTree::SchemaContentArrivedSlot &arrived_slot)
{
  // in windows we use TreeViewAdv feature to expand nodes asynchronously
  // that is this function is already called from a separate thread
  // and it must have items loaded when it returns.
  bool sync= !_grtm->in_main_thread();
  log_debug3("Fetch schema contents for %s\n", schema_name.c_str());
  live_schema_fetch_task->exec(sync,
                               boost::bind(&SqlEditorForm::do_fetch_live_schema_contents, this, _1,
                                           weak_ptr_from(this), node_id, schema_name, include_column_data, arrived_slot));
  
  return true;//!
}


void SqlEditorForm::refresh_live_object_in_overview(wb::LiveSchemaTree::ObjectType type, const std::string schema_name, const std::string old_obj_name, const std::string new_obj_name)
{
  try
  {
    //GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection(); // this lock is acquired by caller
    GMutexLock schema_contents_mutex(_schema_contents_mutex);
    
    bool is_obj_created= old_obj_name.empty();
    bool is_obj_dropped= new_obj_name.empty();
    bool is_obj_renamed= !is_obj_created && !is_obj_dropped && (old_obj_name != new_obj_name);
    if (is_obj_renamed)
      is_obj_created= is_obj_dropped= true;

    // update schema tree even if no object was added/dropped, to clear details attribute which contents might to be changed
    _schema_tree->update_live_object_state(type, schema_name, old_obj_name, new_obj_name);

    // Performing a hard refresh to reflect the changes on the tree
    do_refresh_schema_tree(true);
  }
  CATCH_ANY_EXCEPTION_AND_DISPATCH_TO_DEFAULT_LOG(_("Refresh live schema object"))
}


grt::StringRef SqlEditorForm::do_fetch_live_schema_contents(grt::GRT *grt, Ptr self_ptr, bec::NodeId node_id, const std::string &schema_name, bool include_column_data, wb::LiveSchemaTree::SchemaContentArrivedSlot arrived_slot)
{
  RETVAL_IF_FAIL_TO_RETAIN_WEAK_PTR (SqlEditorForm, self_ptr, self, grt::StringRef(""))
  
  try
  {
    GMutexLock schema_contents_mutex(_schema_contents_mutex);
    
    if (arrived_slot.empty())
      return grt::StringRef("");
    
    LiveSchemaTree::SchemaNode node;
    node.fetched= true;
    node.name= schema_name;
    
    {
      GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
      
      {
        //std::auto_ptr<sql::ResultSet> rs(_aux_dbc_conn->ref->getMetaData()->getSchemaObjects("", schema_name, "table", false));
        std::auto_ptr<sql::ResultSet> rs(_aux_dbc_conn->ref->createStatement()->executeQuery(std::string(sqlstring("SHOW FULL TABLES FROM !", 0) << schema_name)));
        while (rs->next())
        {
          std::string type = rs->getString(2);
          if (type == "VIEW")
          {
            wb::LiveSchemaTree::ViewNode view_node;
            view_node.name= rs->getString(1);
            node.views.push_back(view_node);            
          }
          else
          {
            wb::LiveSchemaTree::TableNode table_node;
            table_node.name= rs->getString(1);
            node.tables.push_back(table_node);
          }
        }
      }
      
      {
        //std::auto_ptr<sql::ResultSet> rs(_aux_dbc_conn->ref->getMetaData()->getSchemaObjects("", schema_name, "routine", false));
        std::auto_ptr<sql::ResultSet> rs(_aux_dbc_conn->ref->createStatement()->executeQuery(std::string(sqlstring("SHOW PROCEDURE STATUS WHERE Db=?", 0) << schema_name)));
        while (rs->next())
        {
          wb::LiveSchemaTree::ObjectNode obj_node;
          obj_node.name= rs->getString(2);
          obj_node.details= " "; // means no details
          node.routines.push_back(obj_node);
        }
      }
      {
        std::auto_ptr<sql::ResultSet> rs(_aux_dbc_conn->ref->createStatement()->executeQuery(std::string(sqlstring("SHOW FUNCTION STATUS WHERE Db=?", 0) << schema_name)));
        while (rs->next())
        {
          wb::LiveSchemaTree::ObjectNode obj_node;
          obj_node.name= rs->getString(2);
          obj_node.details= " "; // means no details
          node.routines.push_back(obj_node);
        }
      }
    }
    
    if (include_column_data)
    {
      for (std::vector<wb::LiveSchemaTree::TableNode>::iterator t = node.tables.begin(); t != node.tables.end(); ++t)
      {
        t->columns_loaded = true;
        fetch_column_data(schema_name, t->name, *t);
      }
      for (std::vector<wb::LiveSchemaTree::ViewNode>::iterator t = node.views.begin(); t != node.views.end(); ++t)
      {
        t->columns_loaded = true;
        fetch_column_data(schema_name, t->name, *t);
      }
    }    
    
    if (arrived_slot)
    {
      arrived_slot(node);
      
      live_schema_fetch_task->execute_in_main_thread(
                                                     boost::bind(&SqlEditorForm::notify_of_fetched_schema_contents,this,  weak_ptr_from(this), node_id),
                                                     false,
                                                     false);
    }
  }
  CATCH_ANY_EXCEPTION_AND_DISPATCH_TO_DEFAULT_LOG(_("Get schema contents"))
  
  return grt::StringRef("");
}

//--------------------------------------------------------------------------------------------------

/**
 * Helper function to generate an HTML line of a column entry in a table definition for the info output.
 */
std::string create_column_table_line(const std::string& name, const std::string& type, const std::string& extra,
  bool isPrimary, bool isIndex)
{
  std::string result = name;

  if (isPrimary || isIndex)
  {
    if (isPrimary)
      result = "<u>" + result + "</u>";
    result = "<b>" + result + "</b>";
  }

  result = "<tr><td style=\"border:none; padding-left: 15px;\">" + result + "</td>";
  result += "<td style=\"border:none; padding-left: 15px;\"><font color='#717171'>" + type +
    (isPrimary ? " PK " : " ") + extra + "</font></td></tr>";
  return result;
}

//--------------------------------------------------------------------------------------------------

void SqlEditorForm::fetch_column_data(const std::string &schema_name, const std::string &obj_name, wb::LiveSchemaTree::ViewNode& view_node)
{
  std::string col_brief_list;
  std::string col_detail_list;

  log_debug3("Fetching column data for %s.%s\n", schema_name.c_str(), obj_name.c_str());  

  try
  {
    GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
    
    std::auto_ptr<sql::Statement> stmt(_aux_dbc_conn->ref->createStatement());
    std::auto_ptr<sql::ResultSet> rs(stmt->executeQuery(std::string(base::sqlstring("SHOW FULL COLUMNS FROM !.!", 0) << schema_name << obj_name)));
    
    while (rs->next())
    {
      std::string column_name= rs->getString(1);
      std::string type = rs->getString(2);
      // 3 collation
      std::string nullable = rs->getString(4);
      std::string key = rs->getString(5);
      std::string default_value = rs->getString(6);
      std::string extra = rs->getString(7);
      
      base::replace(type, "unsigned", "UN");
      
      if (extra != "auto_increment")
        extra = "";
      else
        extra = "AI";
      
      LiveSchemaTree::ColumnNode col_node;
      col_node.name = column_name;
      col_node.is_pk = key == "PRI";
      col_node.is_id = (col_node.is_pk || (nullable == "NO" && key == "UNI"));
      col_node.type = type;
      col_node.default_value = default_value;
      
      view_node.columns.push_back(col_node);
      
      col_brief_list+= (col_brief_list.empty() ? "" : ", ") + column_name;
      col_detail_list += create_column_table_line(column_name, type, extra, key == "PRI", key != "");
    }

    // Clears the flag indicating errors on column load
    view_node.columns_load_error = false;
  }
  catch (const sql::SQLException& exc)
  {
    log_warning("Error fetching column information for '%s'.'%s': %s", schema_name.c_str(), obj_name.c_str(), exc.what());

    // Sets flag indicating error loading columns ( Used for broken views )
    if (exc.getErrorCode() == 1356)
      view_node.columns_load_error = true;
    view_node.details = exc.what();
  }
  
  view_node.columns_loaded = true;
  if (!view_node.columns_load_error)
  {
    view_node.details = strfmt(_("<b>Table:</b> <font color='#148814'><b>%s</b></font><br><br>"), obj_name.c_str());
    view_node.details.append(_("<b>Columns:</b>"));
    view_node.details.append("<table style=\"border: none; border-collapse: collapse;\">" + col_detail_list + "</table><br><br>");
  }
}

void SqlEditorForm::fetch_trigger_data(const std::string &schema_name, const std::string &obj_name, wb::LiveSchemaTree::TableNode& table_node)
{
  try
  {
    GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
    
    std::auto_ptr<sql::Statement> stmt(_aux_dbc_conn->ref->createStatement());
    std::auto_ptr<sql::ResultSet> rs(stmt->executeQuery(std::string(base::sqlstring("SHOW TRIGGERS FROM ! LIKE ?", 0) << schema_name << obj_name)));
    
    while (rs->next())
    {
      wb::LiveSchemaTree::TriggerNode trigger_node;
      
      trigger_node.name= rs->getString(1);
      trigger_node.event_manipulation= wb::LiveSchemaTree::internalize_token(rs->getString(2));
      trigger_node.timing= wb::LiveSchemaTree::internalize_token(rs->getString(5));
      
      table_node.triggers.push_back(trigger_node);
    }
    
    table_node.triggers_loaded = true;
  }
  catch (const sql::SQLException& exc)
  {
    g_warning("Error fetching trigger information for '%s'.'%s': %s", schema_name.c_str(), obj_name.c_str(), exc.what());
  }
}

void SqlEditorForm::fetch_index_data(const std::string &schema_name, const std::string &obj_name, wb::LiveSchemaTree::TableNode& table_node)
{
  try
  {
    GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
    
    std::auto_ptr<sql::Statement> stmt(_aux_dbc_conn->ref->createStatement());
    std::auto_ptr<sql::ResultSet> rs(stmt->executeQuery(std::string(base::sqlstring("SHOW INDEXES FROM !.!", 0) << schema_name << obj_name)));
    
    while (rs->next())
    {
      wb::LiveSchemaTree::IndexColumnNode index_column;
      
      index_column.name = rs->getString(5);
      index_column.nullable = (rs->getString(10) == "YES");
      
      wb::LiveSchemaTree::IndexNode *target_index = table_node.get_index(rs->getString(3));
      
      if( !target_index )
      {
        wb::LiveSchemaTree::IndexNode new_index;
        
        new_index.name = rs->getString(3);
        new_index.type = wb::LiveSchemaTree::internalize_token(rs->getString(11));
        new_index.unique = (rs->getInt(2) == 0);
        
        table_node.indexes.push_back(new_index);
        
        target_index = table_node.get_index(new_index.name);
      }
      
      target_index->columns.push_back(index_column);
    }
    
    table_node.indexes_loaded = true;
  }
  catch (const sql::SQLException& exc)
  {
    g_warning("Error fetching index information for '%s'.'%s': %s", schema_name.c_str(), obj_name.c_str(), exc.what());
  }
}

void SqlEditorForm::fetch_foreign_key_data(const std::string &schema_name, const std::string &obj_name, wb::LiveSchemaTree::TableNode& table_node)
{
  try
  {
    GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
    
    std::auto_ptr<sql::Statement> stmt(_aux_dbc_conn->ref->createStatement());
    std::auto_ptr<sql::ResultSet> rs(stmt->executeQuery(std::string(base::sqlstring("SHOW CREATE TABLE !.!", 0) << schema_name << obj_name)));
    
    while (rs->next())
    {
      wb::LiveSchemaTree::IndexColumnNode index_column;
      
      std::string statement = rs->getString(2);
      
      size_t def_start = statement.find("(");
      size_t def_end = statement.rfind  (")");
      
      std::vector<std::string> def_lines = base::split(statement.substr(def_start, def_end - def_start), "\n");
      
      const char *errptr;
      int erroffs=0;
      const char *pattern = "CONSTRAINT\\s*(\\S*)\\s*FOREIGN KEY\\s*\\((\\S*)\\)\\s*REFERENCES\\s*(\\S*)\\s*\\((\\S*)\\)\\s*((\\w*\\s*)*),?$";
      int patres[64];
      
      pcre *patre= pcre_compile(pattern, 0, &errptr, &erroffs, NULL);
      if (!patre)
        throw std::logic_error("error compiling regex "+std::string(errptr));
      
      std::string fk_name;
      std::string fk_columns;
      std::string fk_ref_table;
      std::string fk_ref_columns;
      std::string fk_rules;
      const char *value;
      
      for(size_t index = 0; index < def_lines.size(); index++)
      {
        int rc = pcre_exec(patre, NULL, def_lines[index].c_str(), def_lines[index].length(), 0, 0, patres, sizeof(patres)/sizeof(int));
        
        if ( rc > 0 )
        {
          // gets the values timestamp and 
          pcre_get_substring(def_lines[index].c_str(), patres, rc, 1, &value);
          fk_name = value;
          
          pcre_get_substring(def_lines[index].c_str(), patres, rc, 2, &value);
          fk_columns = value;
          
          pcre_get_substring(def_lines[index].c_str(), patres, rc, 3, &value);
          fk_ref_table = value;
          
          pcre_get_substring(def_lines[index].c_str(), patres, rc, 4, &value);
          fk_ref_columns = value;
          
          pcre_get_substring(def_lines[index].c_str(), patres, rc, 5, &value);
          fk_rules = value;
          
          // Parses the list fields
          std::vector<std::string> fk_column_list = base::split(fk_columns, ",");
          std::vector<std::string> fk_ref_column_list = base::split(fk_ref_columns, ",");
          std::vector<std::string> fk_rule_tokens = base::split(fk_rules, " ");
          
          // Create the foreign key node
          wb::LiveSchemaTree::FKNode new_fk;
          new_fk.name = base::unquote_identifier(fk_name);
          new_fk.referenced_table = base::unquote_identifier(fk_ref_table);
          
          // Set the default update and delete rules
          new_fk.update_rule = new_fk.delete_rule = wb::LiveSchemaTree::internalize_token("RESTRICT");

          // A rule has at least 3 tokens so the number of tokens could be
          // 3 or 4 for 1 rule and 6,7,8 for two rules, so we get the number with this
          int rule_count = fk_rule_tokens.size() / 3;

          int token_offset = 0;
          for (int index = 0; index < rule_count; index++)
          {
            // Skips the ON token
            token_offset++;

            // Gets the UPDATE/DELETE token
            std::string rule = fk_rule_tokens[token_offset++];

            // Gets the action
            std::string action = fk_rule_tokens[token_offset++];

            if (action == "SET" || action == "NO")
              action += " " + fk_rule_tokens[token_offset++];

            const unsigned char value = wb::LiveSchemaTree::internalize_token(action);

            if (rule == "UPDATE")
              new_fk.update_rule = value;
            else
              new_fk.delete_rule = value;

          }
          
          for(size_t column_index = 0; column_index < fk_column_list.size(); column_index++)
          {
            wb::LiveSchemaTree::FKColumnNode fk_column;
            fk_column.name = base::unquote_identifier(fk_column_list[column_index]);
            fk_column.referenced_column= base::unquote_identifier(fk_ref_column_list[column_index]);
            
            new_fk.columns.push_back(fk_column);
          }
          
          table_node.foreign_keys.push_back(new_fk);
        }
      }
    }
    table_node.foreign_keys_loaded = true;
  }
  catch (const sql::SQLException& exc)
  {
    g_warning("Error fetching foreign key information for '%s'.'%s': %s", schema_name.c_str(), obj_name.c_str(), exc.what());
  }
}


bool SqlEditorForm::fetch_object_details(wb::LiveSchemaTree::ObjectType obj_type, const std::string &schema_name, const std::string &obj_name, short flags, const wb::LiveSchemaTree::ObjectDetailsArrivedSlot &arrived_slot)
{
  log_debug3("Fetching object details for %s.%s\n", schema_name.c_str(), obj_name.c_str());
  if(obj_type == wb::LiveSchemaTree::View || obj_type == wb::LiveSchemaTree::Table)
  {
    LiveSchemaTree::SchemaNode schema_node;
    LiveSchemaTree::ViewNode  view_node;
    LiveSchemaTree::TableNode  table_node;
    
    LiveSchemaTree::ViewNode  *pnode = NULL;
    
    schema_node.fetched= true;
    schema_node.name= schema_name;
    
    if(obj_type == wb::LiveSchemaTree::View)
      pnode = &view_node;
    else
    {
      if (flags & wb::LiveSchemaTree::TRIGGER_DATA)
        fetch_trigger_data(schema_name, obj_name, table_node);
      
      if (flags & wb::LiveSchemaTree::INDEX_DATA)
        fetch_index_data(schema_name, obj_name, table_node);
      
      if (flags & wb::LiveSchemaTree::FK_DATA)
        fetch_foreign_key_data(schema_name, obj_name, table_node);
      
      pnode = &table_node;
    }
    
    if (flags & wb::LiveSchemaTree::COLUMN_DATA)
      fetch_column_data(schema_name, obj_name, *pnode);
    
    pnode->name = obj_name;
    pnode->fetched = true;
    
    arrived_slot(schema_node, pnode, obj_type);
    
    return true;
  }
  
  return false;
}

//--------------------------------------------------------------------------------------------------

/**
 * Activate one or more objects. The term "activate" is a bit misleading as we do other operations too
 * (like clipboard handling).
 */
void SqlEditorForm::tree_activate_objects(const std::string& action,
                                          const std::vector<wb::LiveSchemaTree::ChangeRecord>& changes)
{
  // Most of the activations should lead to a single result (e.g. all clipboard ops go into one string).
  std::string action_modifier; // action can contain prefix denoting action modifier
  std::string real_action= action; // if action modifier is present real_action will store part after w/o modifier prefix
  const char *action_modifier_clipboard= "clipboard_"; // denotes copy of the action result to clipboard
  const char *action_modifier_editor= "editor_"; // denotes insertion of the action result into editor at cursor position
  
  {
    const char* known_modifiers[] = { action_modifier_clipboard, action_modifier_editor };
    for (size_t n= 0, size= sizeof(known_modifiers)/sizeof(known_modifiers[0]); n < size; ++n)
    {
      if (action.find(known_modifiers[n]) == 0)
      {
        action_modifier = known_modifiers[n];
        real_action = action.substr(action_modifier.size());
      }
    }
  }
  
  bool need_refresh = false;
  const char* newline = (changes.size() > 1) ? "\n" : "";
  
  if (real_action == "object_name_short" || real_action == "object_name_long")
  {
    std::string text = "";
    
    for (size_t i = 0; i < changes.size(); i++)
    {
      if (changes[i].type == wb::LiveSchemaTree::Schema)
        text += changes[i].name + newline;
      else
        if (real_action == "object_name_short")
          text += changes[i].name + newline;
        else
          if (real_action == "object_name_long")
            text += strfmt("`%s`.`%s`%s", changes[i].schema.c_str(), changes[i].name.c_str(), newline);
    }
    
    if (action_modifier == action_modifier_clipboard)
      mforms::Utilities::set_clipboard_text(text);
    else if (action_modifier == action_modifier_editor)
      active_sql_editor_or_new_scratch()->insert_text(text);
    return;
  }
  else if (real_action == "object_name_short_columns" || real_action == "object_name_long_columns")
  {
    std::string text = "";
    
    for (size_t i = 0; i < changes.size(); i++)
    {
      if (!text.empty())
        text.append(", ");
      if (real_action == "object_name_short_columns")
        text += changes[i].detail;
      else
        if (real_action == "object_name_long_columns")
          text += strfmt("`%s`.`%s`", changes[i].name.c_str(), changes[i].detail.c_str());
    }
    
    if (action_modifier == action_modifier_clipboard)
      mforms::Utilities::set_clipboard_text(text);
    else if (action_modifier == action_modifier_editor)
      active_sql_editor_or_new_scratch()->insert_text(text);
    return;
  }
  else if (real_action == "table_create_statement")
  {
    std::string text = "";
    for (size_t i = 0; i < changes.size(); i++)
      text += get_object_ddl_script(changes[i].type, changes[i].schema, changes[i].name) + newline;
    
    if (action_modifier == action_modifier_clipboard)
      mforms::Utilities::set_clipboard_text(text);
    else if (action_modifier == action_modifier_editor)
      active_sql_editor_or_new_scratch()->insert_text(text);
  }
  else if (real_action == "table_column_info_statement")
  {
    std::string text = "";
    for (size_t i = 0; i < changes.size(); i++)
      text += strfmt("SHOW COLUMNS FROM `%s`.`%s`;\n", changes[i].schema.c_str(), changes[i].name.c_str());
    
    if (action_modifier == action_modifier_clipboard)
      mforms::Utilities::set_clipboard_text(text);
    else if (action_modifier == action_modifier_editor)
      active_sql_editor_or_new_scratch()->insert_text(text);
    return;
  }
  else if (real_action == "table_delete_statement")
  {
    std::string text = "";
    for (size_t i = 0; i < changes.size(); i++)
      text += strfmt("DELETE FROM `%s`.`%s`\nWHERE <{where_condition}>;\n", changes[i].schema.c_str(), changes[i].name.c_str());
    
    if (action_modifier == action_modifier_clipboard)
      mforms::Utilities::set_clipboard_text(text);
    else if (action_modifier == action_modifier_editor)
      active_sql_editor_or_new_scratch()->insert_text(text);
    return;
  }
  else if (real_action == "table_select_statement" || real_action == "table_insert_statement" ||
           real_action == "table_update_statement")
  {
    typedef std::list<std::pair<std::string, std::string> > ColumnDefinitions;
    ColumnDefinitions column_defs;
    std::string text = "";
    
    for (size_t i = 0; i < changes.size(); i++)
    {
      LiveSchemaTree::ViewNode *view_node = dynamic_cast<LiveSchemaTree::ViewNode*>(
                                              get_schema_tree()->get_object_node_by_type_and_name(changes[i].schema, 
                                                                  changes[i].type,
                                                                  changes[i].name));
      if (view_node && view_node->columns_loaded)
      {
        for (std::vector<LiveSchemaTree::ColumnNode>::const_iterator col = view_node->columns.begin();
             col != view_node->columns.end(); ++col)
          column_defs.push_back(std::make_pair(col->name, col->default_value));
      }
      else
      {
        try
        {
          GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
          std::auto_ptr<sql::ResultSet> rs(_aux_dbc_conn->ref->getMetaData()->getColumns("", changes[i].schema, changes[i].name, "%"));
          while (rs->next())
            column_defs.push_back(std::make_pair(rs->getString(4), rs->getString(6)));
        }
        catch (const sql::SQLException& err)
        {
          log_warning("%s: Error fetching table data for %s.%s: %s", real_action.c_str(),
                      changes[i].schema.c_str(), changes[i].name.c_str(), err.what());
        }
      }

      if (real_action == "table_select_statement")
      {
        std::string columns;
        BOOST_FOREACH (const ColumnDefinitions::value_type &col_def, column_defs)
        columns+= (columns.empty() ? "" : ",\n") + strfmt("`%s`.`%s`", changes[i].name.c_str(), col_def.first.c_str());
        text += strfmt("SELECT\n%s\nFROM `%s`.`%s`;\n", columns.c_str(), changes[i].schema.c_str(), changes[i].name.c_str());
      }
      
      else if (real_action == "table_insert_statement")
      {
        std::string columns;
        std::string values;
        BOOST_FOREACH (const ColumnDefinitions::value_type &col_def, column_defs)
        {
          columns+= (columns.empty() ? "" : ",\n") + strfmt("`%s`", col_def.first.c_str());
          values+= (values.empty() ? "" : ",\n") + strfmt("<{%s: %s}>", col_def.first.c_str(), col_def.second.c_str());
        }
        text += strfmt("INSERT INTO `%s`.`%s`\n(%s)\nVALUES\n(\n%s\n);\n", changes[i].schema.c_str(), changes[i].name.c_str(), columns.c_str(), values.c_str());
      }
      
      else if (real_action == "table_update_statement")
      {
        std::string columns;
        BOOST_FOREACH (const ColumnDefinitions::value_type &col_def, column_defs)
        columns+= (columns.empty() ? "" : ",\n") + strfmt("`%s` = {%s: %s}", col_def.first.c_str(), col_def.first.c_str(), col_def.second.c_str());
        text += strfmt("UPDATE `%s`.`%s`\nSET\n%s\nWHERE <{where_condition}>;\n", changes[i].schema.c_str(), changes[i].name.c_str(), columns.c_str());
      }
      
      text += std::string(newline) + newline;
    }
    
    if (action_modifier == action_modifier_clipboard)
      mforms::Utilities::set_clipboard_text(text);
    else if (action_modifier == action_modifier_editor)
      active_sql_editor_or_new_scratch()->insert_text(text);
    
    return;
  }
  else if (real_action == "table_select_statement_columns" || real_action == "table_insert_statement_columns" ||
           real_action == "table_update_statement_columns" ||
           real_action == "edit_data_columns" || real_action == "select_data_columns")
  {
    typedef std::string TableName;
    typedef std::string ColumnName;
    std::map<TableName, std::map<ColumnName, std::string> > table_column_types;
    std::string text;
    typedef std::map<TableName, std::string> TableStringMap;
    TableStringMap first_text;
    TableStringMap second_text;

    // cache default values for tables
    for (size_t i = 0; i < changes.size(); i++)
    {
      TableName full_table_name = sqlstring("!.!", 0) << changes[i].schema << changes[i].name;
      std::string column_type;
      std::map<TableName, std::map<ColumnName, std::string> >::iterator iter = table_column_types.find(full_table_name);
      
      if (real_action != "table_select_statement_columns" && real_action != "edit_data_columns" && real_action != "select_data_columns")
      {
        if (iter == table_column_types.end())
        {
          std::map<ColumnName, std::string> column_types;
          LiveSchemaTree::ViewNode *view_node = dynamic_cast<LiveSchemaTree::ViewNode*>(
                                                  get_schema_tree()->get_object_node_by_type_and_name(changes[i].schema, 
                                                                                    changes[i].type,
                                                                                    changes[i].name));      
          if (view_node && view_node->columns_loaded)
          {
            for (std::vector<LiveSchemaTree::ColumnNode>::const_iterator col = view_node->columns.begin();
                 col != view_node->columns.end(); ++col)
            {
              if (real_action == "table_insert_statement_columns")
                column_types[col->name] = col->default_value;
              else
                column_types[col->name] = col->type;
            }
            table_column_types[full_table_name] = column_types;
          }
          else
          {
            try
            {
              GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
              int idx = (real_action == "table_insert_statement_columns") ? 13 : 6; // default : type
              std::auto_ptr<sql::ResultSet> rs(_aux_dbc_conn->ref->getMetaData()->getColumns("", changes[i].schema, changes[i].name, "%"));
              while (rs->next())
                column_types[rs->getString(4)] = rs->getString(idx);
              table_column_types[full_table_name] = column_types;
            }
            catch (const sql::SQLException& err)
            {
              log_warning("%s: Error fetching table data for %s.%s: %s", real_action.c_str(),
                          changes[i].schema.c_str(), changes[i].name.c_str(), err.what());
            }
          }
          
          if (column_types.find(changes[i].detail) != column_types.end())
            column_type = column_types[changes[i].detail];
        }
        else
        {
          if (iter->second.find(changes[i].detail) != iter->second.end())
            column_type = iter->second[changes[i].detail];
        }
      }

      if (real_action == "table_select_statement_columns" || real_action == "edit_data_columns" || real_action == "select_data_columns")
      {
        if (!first_text[full_table_name].empty())
          first_text[full_table_name].append(", ");
        first_text[full_table_name].append(changes[i].detail);
      }
      else if (real_action == "table_insert_statement_columns")
      {
        if (!first_text[full_table_name].empty())
          first_text[full_table_name].append(", ");
        first_text[full_table_name].append(changes[i].detail);
        
        if (!second_text[full_table_name].empty())
          second_text[full_table_name].append(", ");
        second_text[full_table_name].append(strfmt("<{%s}>", column_type.c_str())); // actually is default value
      }
      else if (real_action == "table_update_statement_columns")
      {
        if (!first_text[full_table_name].empty())
          first_text[full_table_name].append(", ");
        first_text[full_table_name].append(strfmt("%s = <{%s}>", changes[i].detail.c_str(), column_type.c_str()));        
      }
    }

    if (real_action == "table_select_statement_columns" || real_action == "edit_data_columns" || real_action == "select_data_columns")
    {
      BOOST_FOREACH(const TableStringMap::value_type &table_columns, first_text)
        text += strfmt("SELECT %s\nFROM %s;\n", table_columns.second.c_str(), table_columns.first.c_str());
    }
    else if (real_action == "table_insert_statement_columns")
    {
      BOOST_FOREACH (const TableStringMap::value_type &table_columns, first_text)
        text += strfmt("INSERT INTO %s (%s)\nVALUES\n(\n%s\n);\n", table_columns.first.c_str(), table_columns.second.c_str(),
                       second_text[table_columns.first].c_str());
    }
    else if (real_action == "table_update_statement_columns")
    {
      BOOST_FOREACH (const TableStringMap::value_type &table_columns, first_text)
        text += strfmt("UPDATE %s\nSET\n%s\nWHERE <{where_condition}>;\n", table_columns.first.c_str(), table_columns.second.c_str());
    }
    
    if (real_action == "edit_data_columns" || real_action == "select_data_columns")
    {
      need_refresh = true;
      
      run_sql_in_scratch_tab(text, false, real_action == "edit_data_columns");
    }
    else
    {
      text += std::string(newline) + newline;
      
      if (action_modifier == action_modifier_clipboard)
        mforms::Utilities::set_clipboard_text(text);
      else if (action_modifier == action_modifier_editor)
        active_sql_editor_or_new_scratch()->insert_text(text);
      
      return;
    }
  }
  
  for (size_t i = 0; i < changes.size(); i++)
  {
    std::string sql;
    switch (changes[i].type)
    {
      case wb::LiveSchemaTree::Schema:
        if (real_action == "filter")
        {
          _side_bar->get_filter_entry()->set_value(changes[i].name);
          (*_side_bar->get_filter_entry()->signal_changed())();
        }
        else
        {
          active_schema(changes[i].name);
          _schema_tree->expand_node(active_schema_index());
          if (_side_bar)
            _side_bar->expand_schema(active_schema_index());
        }
        break;
      case wb::LiveSchemaTree::Table:
      case wb::LiveSchemaTree::View:
        if (real_action == "activate" || real_action == "edit_data" || real_action == "select_data")
          sql = sqlstring("SELECT * FROM !.!;", base::QuoteOnlyIfNeeded) << changes[i].schema << changes[i].name;
        break;
      default:
        break;
    }
    
    if (!sql.empty())
    {
      need_refresh = true;
      
      if (real_action == "edit_data")
      {
        int ed = run_sql_in_scratch_tab(sql, false, true);
        _sql_editors[ed]->caption = changes[i].name;
        do_partial_ui_refresh(SqlEditorForm::RefreshEditorTitle);
      }
      else
        run_sql_in_scratch_tab(sql, false, false);
    }
  }
  if (need_refresh)
    do_partial_ui_refresh(SqlEditorForm::RefreshEditorBackend);
}

//--------------------------------------------------------------------------------------------------

/**
 * Convenience API for the activation interface.
 */
void SqlEditorForm::schema_object_activated(const std::string &action, wb::LiveSchemaTree::ObjectType type,
                                            const std::string &schema, const std::string &name)
{
  std::vector<wb::LiveSchemaTree::ChangeRecord> changes;
  wb::LiveSchemaTree::ChangeRecord record = { type, schema, name };
  changes.push_back(record);
  tree_activate_objects(action, changes);
}

//--------------------------------------------------------------------------------------------------

/**
 * Starts altering more than one db object.
 */
void SqlEditorForm::tree_alter_objects(const std::vector<wb::LiveSchemaTree::ChangeRecord>& changes)
{
  for (size_t i = 0; i < changes.size(); i++)
    do_alter_live_object(changes[i].type, changes[i].schema, changes[i].name);
}

//--------------------------------------------------------------------------------------------------

void SqlEditorForm::do_alter_live_object(wb::LiveSchemaTree::ObjectType type, const std::string &schema_name, const std::string &aobj_name)
{
  std::string used_schema_name = schema_name;
  std::string obj_name = aobj_name;
  try
  {
    db_mgmt_RdbmsRef rdbms= this->rdbms();
    std::string database_package= *rdbms->databaseObjectPackage();
    
    db_CatalogRef client_state_catalog= _grtm->get_grt()->create_object<db_Catalog>(database_package + ".Catalog");
    client_state_catalog->name("default");
    client_state_catalog->oldName("default");
    client_state_catalog->version(rdbms->version());
    grt::replace_contents(client_state_catalog->simpleDatatypes(), rdbms->simpleDatatypes());
    //XXX this should be changed when/if global userDatatypes are added
    //XXX    grt::replace_contents(client_state_catalog->userDatatypes(), 
    //XXX                          workbench_physical_ModelRef::cast_from(_live_physical_overview->get_model())->catalog()->userDatatypes());
    
    db_SchemaRef schema;
    if (wb::LiveSchemaTree::Schema != type)
    {
      
      
      if (used_schema_name == "")
        used_schema_name = _aux_dbc_conn->active_schema;
      
      if (used_schema_name == "")
      {
        mforms::Utilities::show_warning(strfmt(_("No Schema Selected")),
                                        _("A default schema must be set by double clicking its name in the SCHEMA list."),
                                        _("OK"));
        
        return;
      }
      else
      {
        schema= _grtm->get_grt()->create_object<db_Schema>(database_package + ".Schema");
        schema->owner(client_state_catalog);
        
        schema->name(used_schema_name);
        schema->oldName(used_schema_name);
        client_state_catalog->schemata().insert(schema);
        client_state_catalog->defaultSchema(schema);
      }
    }
    
    bool is_object_new= obj_name.empty();
    
    std::string ddl_script;
    std::string sql_mode;
    if (!is_object_new)
    {
      // parse selected object DDL into auxiliary catalog
      ddl_script= get_object_ddl_script(type, used_schema_name, obj_name);
      if (ddl_script.empty())
        return;
      {
        GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
        get_session_variable(_aux_dbc_conn, "sql_mode", sql_mode);
      }
      
      if (!parse_ddl_into_catalog(rdbms, client_state_catalog, strfmt("`%s`.`%s`", schema_name.c_str(), obj_name.c_str()), ddl_script, sql_mode))
        return;
    }
    
    db_CatalogRef server_state_catalog(db_CatalogRef::cast_from(grt::copy_object(_grtm->get_grt(), client_state_catalog)));
    
  retry_search:
    db_DatabaseObjectRef db_object;
    switch (type)
    {
      case wb::LiveSchemaTree::Schema:
        db_object= is_object_new ?
        create_new_schema(client_state_catalog) :
        find_named_object_in_list(client_state_catalog->schemata(), obj_name);
        break;
      case wb::LiveSchemaTree::Table:
        db_object= is_object_new ?
        create_new_table(schema) :
        find_named_object_in_list(schema->tables(), obj_name);
        break;
      case wb::LiveSchemaTree::View:
        db_object= is_object_new ?
        create_new_view(schema) :
        find_named_object_in_list(schema->views(), obj_name);
        break;
      case wb::LiveSchemaTree::Routine:
        db_object= is_object_new ?
        create_new_routine(schema) :
        find_named_object_in_list(schema->routines(), obj_name);
        break;
    }
    if (!db_object.is_valid())
    {
      std::string lower;
      // There is a bug in server that causes get_object_ddl_script() for uppercase named tables
      // to be returned in lowercase, in osx. http://bugs.mysql.com/bug.php?id=57830
      // So, if you try to alter FOOBAR, it will not find it, since the table will be revenged
      // in lowercase. To work around that, we convert the object name to lowercase and repeat
      // the search if the 1st try didn't work.
      lower = tolower(obj_name);
      if (lower != obj_name)
      {
        g_warning("Object name %s was not found in catalog, trying to search it as %s",
                  obj_name.c_str(), lower.c_str());
        obj_name = lower;
        goto retry_search;
      }
      else
        g_warning("Object name %s was not found in catalog.", aobj_name.c_str());
      
      return;
    }
    db_object->customData().set("liveRdbms", rdbms);
    db_object->customData().set("clientStateCatalog", client_state_catalog);
    db_object->customData().set("serverStateCatalog", server_state_catalog);
    db_object->customData().set("originalObjectDDL", grt::StringRef(ddl_script));
    db_object->customData().set("sqlMode", grt::StringRef(sql_mode));
    {
      std::ostringstream oss;
      oss << this;
      db_object->customData().set("contextDbSqlEditor", grt::StringRef(oss.str()));
    }
    
    // TODO: make docking/non-docking switchable via preferences.
    //_context_ui->get_wb()->open_object_editor(db_object, bec::StandaloneWindowFlag);
    _context_ui->get_wb()->open_object_editor(db_object, bec::ForceNewWindowFlag);
  }
  catch (const std::exception & e)
  {
    mforms::Utilities::show_error(strfmt(_("Failed to create/alter `%s`.`%s`"), used_schema_name.c_str(), obj_name.c_str()), e.what(), _("OK"));
  }
}


db_SchemaRef SqlEditorForm::create_new_schema(db_CatalogRef owner)
{
  db_SchemaRef object= _grtm->get_grt()->create_object<db_Schema>(owner->schemata()->content_type_spec().object_class);
  object->owner(owner);
  object->name("new_schema");
  owner->schemata().insert(object);
  owner->defaultSchema(object);
  return object;
}


db_TableRef SqlEditorForm::create_new_table(db_SchemaRef owner)
{
  db_TableRef object= _grtm->get_grt()->create_object<db_Table>(owner->tables()->content_type_spec().object_class);
  object->owner(owner);
  object->name("new_table");
  owner->tables().insert(object);
  return object;
}


db_ViewRef SqlEditorForm::create_new_view(db_SchemaRef owner)
{
  db_ViewRef object= _grtm->get_grt()->create_object<db_View>(owner->views()->content_type_spec().object_class);
  object->owner(owner);
  object->name("new_view");
  owner->views().insert(object);
  return object;
}


db_RoutineRef SqlEditorForm::create_new_routine(db_SchemaRef owner)
{
  db_RoutineRef object= _grtm->get_grt()->create_object<db_Routine>(owner->routines()->content_type_spec().object_class);
  object->owner(owner);
  object->name("new_routine");
  owner->routines().insert(object);
  return object;
}


void SqlEditorForm::tree_create_object(wb::LiveSchemaTree::ObjectType type, const std::string &schema_name, const std::string &obj_name)
{
  do_alter_live_object(type, schema_name, obj_name);
}

//--------------------------------------------------------------------------------------------------

/**
 * Drops one or more objects of any supported type and from multiple schemas.
 */
void SqlEditorForm::tree_drop_objects(const std::vector<wb::LiveSchemaTree::ChangeRecord>& changes)
{
  std::string sql = "";
  RowId log_id = add_log_message(DbSqlEditorLog::BusyMsg, "Preparing...", _("Drop selected objects"), "");
  
  try
  {
    // Do two loops here: one without schemas and another one only schemas. This way we don't try to
    // delete objects twice. This could be made smarter by not dropping objects whose owning schema
    // is dropped. But due to case sensitivity issue this is more complex, so we go the simple route.
    for (size_t i = 0; i < changes.size(); i++)
    {
      switch (changes[i].type)
      {
        case wb::LiveSchemaTree::Schema:
          // Handle in second loop.
          break;
        case wb::LiveSchemaTree::Table:
          sql += sqlstring("drop table !.!;\n", 0) << changes[i].schema << changes[i].name;
          break;
        case wb::LiveSchemaTree::View:
          sql += sqlstring("drop view !.!;\n", 0) << changes[i].schema << changes[i].name;
          break;
        case wb::LiveSchemaTree::Routine:
        {
          std::string query_sql= sqlstring("select routine_type from information_schema.routines where "
                                        "routine_schema=? and routine_name = ?", 0) << changes[i].schema << changes[i].name;
          
          {
            GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
            std::auto_ptr<sql::Statement> stmt(_aux_dbc_conn->ref->createStatement());
            boost::shared_ptr<sql::ResultSet> rs(stmt->executeQuery(query_sql));
            
            // There should really be only one (or no) record.
            while (rs->next())
              if (rs->getString(1) == "PROCEDURE")
                sql += sqlstring("drop procedure !.!;\n", 0) << changes[i].schema << changes[i].name;
              else
                sql += sqlstring("drop function !.!;\n", 0) << changes[i].schema << changes[i].name;
          }
        }
          break;
      }
    }
    
    for (size_t i = 0; i < changes.size(); i++)
    {
      if (changes[i].type== LiveSchemaTree::Schema)
        sql += sqlstring("drop schema !;\n", 0) << changes[i].name;
    }
    
    if (sql.size() > 0)
    {
      if (run_live_object_alteration_wizard(sql, NULL, log_id, sql))
      {
        std::string last_schema = "";
        for (size_t i = 0; i < changes.size(); i++)
        {
          refresh_live_object_in_overview(changes[i].type, changes[i].schema, changes[i].name.c_str(), "");
          if (changes[i].type == LiveSchemaTree::Schema)
            last_schema = changes[i].name;
        }
        
        if ((last_schema.size() > 0) && (active_schema() == last_schema) && _connection.is_valid())
        {
          std::string default_schema= _connection->parameterValues().get_string("schema", "");
          if (!default_schema.empty())
          {
            _grtm->get_dispatcher()->call_from_main_thread<void>(
                                                                 boost::bind((void(SqlEditorForm::*)(const std::string&))&SqlEditorForm::active_schema, this, boost::cref(default_schema)),
                                                                 false/*wait*/,
                                                                 true/*force_queue*/);
          }
        }
      }
    }
  }
  catch (const std::exception &e)
  {
    set_log_message(log_id, DbSqlEditorLog::ErrorMsg, 
                    strfmt(_("An error occured while processing a drop request.\n\nQuery: %s\n\nError: %s"), sql.c_str(), e.what()),
                    sql, "");
    mforms::Utilities::show_error(_("Running drop query failed"),
                                  strfmt(_("An error occured while processing a drop request.\n\nQuery: %s\n\nError: %s"), sql.c_str(), e.what()), _("OK"));
  }
}

//--------------------------------------------------------------------------------------------------

std::string SqlEditorForm::get_object_ddl_script(wb::LiveSchemaTree::ObjectType type, const std::string &schema_name, const std::string &obj_name)
{
  const size_t DDL_COLUMN= 5;
  std::string delimiter;
  {
    SqlFacade::Ref sql_facade= SqlFacade::instance_for_rdbms(rdbms());
    Sql_specifics::Ref sql_specifics= sql_facade->sqlSpecifics();
    delimiter= sql_specifics->non_std_sql_delimiter();
  }
  std::string ddl_script;
  //triggers are fetched prior to table ddl, but should appear after table created
  std::string additional_ddls;
  ddl_script+= strfmt("delimiter %s\n\n", delimiter.c_str());
  
  try
  {
    GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
    
    std::auto_ptr<sql::ResultSet> ddl_rs;
    
    switch (type)
    {
      case wb::LiveSchemaTree::Schema:
        ddl_rs.reset(_aux_dbc_conn->ref->getMetaData()->getSchemaObjects("", obj_name, "schema", true, obj_name));
        break;
        
      case wb::LiveSchemaTree::Table:
      {
        // triggers
        std::auto_ptr<sql::ResultSet> rs(_aux_dbc_conn->ref->getMetaData()->getSchemaObjects("", schema_name, "trigger", true, "", obj_name));
        while (rs->next())
        {
          additional_ddls+= rs->getString(DDL_COLUMN);
          additional_ddls+= delimiter + "\n\n";
        }
      }
        ddl_rs.reset(_aux_dbc_conn->ref->getMetaData()->getSchemaObjects("", schema_name, "table", true, obj_name));
        break;
        
      case wb::LiveSchemaTree::View:
        ddl_rs.reset(_aux_dbc_conn->ref->getMetaData()->getSchemaObjects("", schema_name, "view", true, obj_name));
        break;
        
      case wb::LiveSchemaTree::Routine:
        ddl_rs.reset(_aux_dbc_conn->ref->getMetaData()->getSchemaObjects("", schema_name, "routine", true, obj_name));
        break;
    }
    
    while (ddl_rs->next())
    {
      ddl_script+= ddl_rs->getString(DDL_COLUMN);
      ddl_script+= delimiter + "\n\n";
    }
    ddl_script+= additional_ddls;
  }
  catch (const sql::SQLException &)
  {
    ddl_script.clear();
  }
  return ddl_script;
}


void SqlEditorForm::refresh_live_object_in_editor(bec::DBObjectEditorBE* obj_editor, bool using_old_name)
{
  db_DatabaseObjectRef db_object= obj_editor->get_dbobject();
  
  db_CatalogRef client_state_catalog= db_CatalogRef::cast_from(db_object->customData().get("clientStateCatalog"));
  
  std::string obj_name= using_old_name ? db_object->oldName() : db_object->name();
  // don't refresh new objects that where not applied yet
  if (obj_name.empty())
    return;
  db_object->name(obj_name);
  db_object->oldName("");
  
  std::string schema_name= db_SchemaRef::can_wrap(db_object) ? std::string() : *db_object->owner()->name();
  db_SchemaRef schema;
  if (!schema_name.empty())
    schema= db_SchemaRef::cast_from(db_object->owner());
  
  wb::LiveSchemaTree::ObjectType db_object_type;
  
  if (db_SchemaRef::can_wrap(db_object))
  {
    db_object_type= wb::LiveSchemaTree::Schema;
  }
  else
  {
    if (db_TableRef::can_wrap(db_object))
    {
      db_object_type= wb::LiveSchemaTree::Table;
      
      // reset selection of fkeys/indices to avoid warnings
      bec::TableEditorBE *table_editor= dynamic_cast <bec::TableEditorBE *> (obj_editor);
      table_editor->get_fks()->select_fk(NodeId());
      table_editor->get_indexes()->select_index(NodeId());
      
      db_TableRef table= db_TableRef::cast_from(db_object);
      table->isStub(1);
      table->triggers().remove_all();
      table->foreignKeys().remove_all();
      table->indices().remove_all();
      table->columns().remove_all();
    }
    else if (db_ViewRef::can_wrap(db_object))
    {
      db_object_type= wb::LiveSchemaTree::View;
    }
    else if (db_RoutineRef::can_wrap(db_object))
    {
      db_object_type= wb::LiveSchemaTree::Routine;
    }
  }
  
  // reparse object's DDL
  std::string ddl_script;
  std::string sql_mode;
  {
    ddl_script= get_object_ddl_script(db_object_type, schema_name, obj_name);
    if (!ddl_script.empty())
    {
      db_object->oldName(obj_name);
      
      try
      {
        GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
        get_session_variable(_aux_dbc_conn, "sql_mode", sql_mode);
      }
      CATCH_ANY_EXCEPTION_AND_DISPATCH(_("Get 'sql_mode' session variable"));
      
      parse_ddl_into_catalog(obj_editor->get_rdbms(), client_state_catalog, 
                             strfmt("`%s`.`%s`", schema_name.c_str(), obj_name.c_str()),
                             ddl_script, sql_mode);
    }
  }
  
  {
    db_CatalogRef server_state_catalog(db_CatalogRef::cast_from(grt::copy_object(_grtm->get_grt(), client_state_catalog)));
    db_object->customData().set("serverStateCatalog", server_state_catalog);
  }
  db_object->customData().set("originalObjectDDL", grt::StringRef(ddl_script));
  db_object->customData().set("sqlMode", grt::StringRef(sql_mode));
  
  // enable refresh of sql editor contents
  Sql_editor::Ref active_sql_editor= obj_editor->get_sql_editor();
  if (active_sql_editor)
  {
    active_sql_editor->is_refresh_enabled(true);
    active_sql_editor->is_sql_check_enabled(true);
    // provoke database object to refresh FE control contents
    (*db_object->signal_changed())("", grt::ValueRef());
  }
}


bool SqlEditorForm::parse_ddl_into_catalog(db_mgmt_RdbmsRef rdbms, db_CatalogRef client_state_catalog, 
                                           const std::string &obj_descr, const std::string &ddl_script, std::string sql_mode)
{
  SqlFacade::Ref sql_facade= SqlFacade::instance_for_rdbms(rdbms);
  Sql_parser::Ref sql_parser= sql_facade->sqlParser();
  sql_parser->messages_enabled(false);
  grt::DictRef options(_grtm->get_grt());
  options.set("reuse_existing_objects", grt::IntegerRef(1));
  if (!sql_mode.empty())
    options.gset("sql_mode", sql_mode);
  
  int err_count= sql_parser->parse_sql_script(client_state_catalog, ddl_script, options);
  bool generic_parse_error = false;
  
  //! dbms-specific code
  if (options.has_key("sql_mode") && (0 < err_count))
  {
    if (std::string::npos != sql_mode.find("ANSI_QUOTES"))
      sql_mode= replace_string(sql_mode, "ANSI_QUOTES", "");
    else
      sql_mode+= ",ANSI_QUOTES";
    options.gset("sql_mode", sql_mode);
    options.set("reuse_existing_objects", grt::IntegerRef(1));
    err_count= sql_parser->parse_sql_script(client_state_catalog, ddl_script, options);
    if (0 == err_count)
    {
      if (mforms::Utilities::show_warning(strfmt(_("Error Parsing DDL for %s"), obj_descr.c_str()),
                                          _("The object's DDL retrieved from DBMS is inconsistent with SQL_MODE variable set for the connection. "
                                            "In particular current state of ANSI_QUOTES flag contradicts to what it was set when the object was being created. "
                                            "This may lead to failure when trying to apply changes. "
                                            "As a workaround you might want to temporarily change the SQL_MODE variable to its previous value."),
                                          _("OK"), _("View DDL")) != mforms::ResultOk)
      {
        new_sql_scratch_area();
        set_sql_editor_text(ddl_script);
      }
      return false;
    }
    else
      generic_parse_error = true;
  } 
  else if (err_count > 0)
    generic_parse_error = true;
  
  if (generic_parse_error)
  {
    if (mforms::Utilities::show_error(strfmt(_("Error Parsing DDL for %s"), obj_descr.c_str()),
                                      _("There was an error parsing the DDL retrieved from the DBMS."),
                                      _("OK"), _("View DDL")) != mforms::ResultOk)
    {
      new_sql_scratch_area();
      set_sql_editor_text(ddl_script);
    }
    return false;
  }
  
  return true;
}


void SqlEditorForm::notify_of_ui_form_creation(bec::UIForm *form)
{
  DBObjectEditorBE *editor= dynamic_cast <DBObjectEditorBE *> (form);
  if (editor)
  {
    editor->on_apply_changes_to_live_object= boost::bind(&SqlEditorForm::apply_changes_to_object, this, _1, _2);
    editor->on_refresh_live_object= boost::bind(&SqlEditorForm::refresh_live_object_in_editor, this, _1, true);
    editor->on_create_live_table_stubs= boost::bind(&SqlEditorForm::create_live_table_stubs, this, _1);
    editor->on_expand_live_table_stub= boost::bind(&SqlEditorForm::expand_live_table_stub, this, _1, _2, _3);
  }
}


template <typename T>
static bool remove_model_only_objects(grt::ListRef<T> obj_list)
{
  for (int n= obj_list.count()-1;  n >= 0; --n)
    if (obj_list.get(n)->modelOnly())
      obj_list.remove(n);
  return true;
}

static bool remove_model_only_objects(db_TableRef table)
{
  //remove_model_only_objects(table->columns()); // modelOnly is not a member of db_Column
  remove_model_only_objects(table->triggers());
  //remove_model_only_objects(table->indices()); // modelOnly is not a member of db_Index
  remove_model_only_objects(table->foreignKeys());
  return true;
}

static bool remove_model_only_objects(db_SchemaRef schema)
{
  remove_model_only_objects(schema->tables());
  schema->tables().foreach(std::ptr_fun((bool(*)(db_TableRef))&remove_model_only_objects));
  remove_model_only_objects(schema->views());
  remove_model_only_objects(schema->routines());
  return true;
}

static bool remove_model_only_objects(db_CatalogRef catalog)
{
  remove_model_only_objects(catalog->schemata());
  catalog->schemata().foreach(std::ptr_fun((bool(*)(db_SchemaRef))&remove_model_only_objects));
  return true;
}

bool SqlEditorForm::apply_changes_to_object(bec::DBObjectEditorBE* obj_editor, bool dry_run)
{
  std::string log_descr;
  RowId log_id = -1;
  if (!dry_run)
  {
    log_descr = strfmt(_("Apply changes to %s"), obj_editor->get_name().c_str());
    log_id = add_log_message(DbSqlEditorLog::BusyMsg, "Preparing...", log_descr, "");
  }
  try
  {
    if (!dry_run && obj_editor->get_sql_editor()->has_sql_errors())
    {
      int res= mforms::Utilities::show_warning(
                                               _("Apply Changes to Object"),
                                               _("The object's DDL statement contains syntax errors.\n"
                                                 "Are you sure you want to apply the DDL statement unchanged?"),
                                               _("Yes"),
                                               _("No"));
      
      if (res != mforms::ResultOk)
      {
        set_log_message(log_id, DbSqlEditorLog::ErrorMsg, "Cancelled", log_descr, "");
        return false;
      }
    }
    
    db_DatabaseObjectRef db_object= obj_editor->get_dbobject();
    
    // check for name conflicts
    // if the object is new or its name was changed
    if (!dry_run)
    {
      bool case_sensitive_identifiers= (_options.get_int("SqlIdentifiersCS", 1) != 0);
      std::string obj_name= db_object->name();
      std::string obj_old_name= db_object->oldName();
      if (!case_sensitive_identifiers)
      {
        obj_name = tolower(obj_name);
        obj_old_name = tolower(obj_old_name);
      }
      if (obj_name != obj_old_name)
      {
        std::list<std::string> obj_types;
        {
          if (db_SchemaRef::can_wrap(db_object))
          {
            obj_types.push_back("schema");
          }
          else if (db_TableRef::can_wrap(db_object) || db_ViewRef::can_wrap(db_object))
          {
            obj_types.push_back("table");
            obj_types.push_back("view");
          }
          else if (db_RoutineRef::can_wrap(db_object))
          {
            obj_types.push_back("routine");
          }
        }
        std::string schema_name= db_SchemaRef::can_wrap(db_object) ? std::string() : *db_object->owner()->name();
        BOOST_FOREACH (const std::string &obj_type, obj_types)
        {
          std::auto_ptr<sql::ResultSet> rs(_aux_dbc_conn->ref->getMetaData()->getSchemaObjects("", schema_name, obj_type, false, *db_object->name()));
          if (rs->next())
          {
            mforms::Utilities::show_error(
                                          _("Apply Changes to Object"),
                                          strfmt(_("Selected name conflicts with existing %s `%s`."), obj_type.c_str(), (*db_object->name()).c_str()),
                                          _("OK"));
            set_log_message(log_id, DbSqlEditorLog::ErrorMsg, 
                            strfmt(_("Selected name conflicts with existing %s `%s`."), obj_type.c_str(), (*db_object->name()).c_str()),
                            log_descr, "");
            return false;
          }
        }
      }
    }
    
    DbMySQLImpl *diffsql_module= _grtm->get_grt()->find_native_module<DbMySQLImpl>("DbMySQL"); //! should be db-agnostic
    
    db_CatalogRef server_cat= db_CatalogRef::cast_from(db_object->customData().get("serverStateCatalog"));
    db_CatalogRef client_cat= db_CatalogRef::cast_from(db_object->customData().get("clientStateCatalog"));
    db_mgmt_RdbmsRef rdbms= db_mgmt_RdbmsRef::cast_from(db_object->customData().get("liveRdbms"));
    
    db_CatalogRef client_cat_copy= db_CatalogRef::cast_from(grt::copy_object(_grtm->get_grt(), client_cat));
    db_CatalogRef server_cat_copy= db_CatalogRef::cast_from(grt::copy_object(_grtm->get_grt(), server_cat));
    
    remove_model_only_objects(client_cat_copy);
    remove_model_only_objects(server_cat_copy);
    
    std::string alter_script= diffsql_module->makeAlterScriptForObject(server_cat_copy, client_cat_copy, db_object);
    
    if (alter_script.empty())
    {
      if (!dry_run && !on_sql_script_run_error.empty())
        on_sql_script_run_error(log_id, _("No changes to object were detected."), "");
      if (!dry_run)
        set_log_message(log_id, DbSqlEditorLog::NoteMsg, _("No changes detected"), log_descr, "");
      return false; // no changes detected
    }
    
    if (dry_run)
      return true; // some changes were detected
    
    //bool is_live_object_alteration_wizard_enabled= (0 != _options.get_int("DbSqlEditor:IsLiveObjectAlterationWizardEnabled", 1));
    if (true/*is_live_object_alteration_wizard_enabled*/)
    {
      run_live_object_alteration_wizard(alter_script, obj_editor, log_id, log_descr);
    }
    else
    {
      apply_object_alter_script(alter_script, obj_editor, log_id);
    }
  }
  catch (const std::exception &e)
  {
    if (!on_sql_script_run_error.empty())
      on_sql_script_run_error(log_id, e.what(), "");
    set_log_message(log_id, DbSqlEditorLog::ErrorMsg, e.what(), log_descr, "");
    log_error("Exception applying changes to live object: %s\n", e.what());
  }
  return true; // some changes were detected and applied
}


bool SqlEditorForm::run_live_object_alteration_wizard(const std::string &alter_script, bec::DBObjectEditorBE* obj_editor, RowId log_id,
                                                      const std::string &log_context)
{
  on_sql_script_run_error.disconnect_all_slots();
  on_sql_script_run_progress.disconnect_all_slots();
  on_sql_script_run_statistics.disconnect_all_slots();
  
  SqlScriptRunWizard wizard(_grtm);
  scoped_connection c1(on_sql_script_run_error.connect(boost::bind(&SqlScriptApplyPage::on_error, wizard.apply_page, _1, _2, _3)));
  scoped_connection c2(on_sql_script_run_progress.connect(boost::bind(&SqlScriptApplyPage::on_exec_progress, wizard.apply_page, _1)));
  scoped_connection c3(on_sql_script_run_statistics.connect(boost::bind(&SqlScriptApplyPage::on_exec_stat, wizard.apply_page, _1, _2)));
  
  std::string errors;
  
  scoped_connection c4(on_sql_script_run_error.connect(boost::bind(&SqlEditorForm::sql_script_apply_error, this, _1, _2, _3, boost::ref(errors))));
  scoped_connection c5(on_sql_script_run_progress.connect(boost::bind(&SqlEditorForm::sql_script_apply_progress, this, _1)));
  scoped_connection c6(on_sql_script_run_statistics.connect(boost::bind(&SqlEditorForm::sql_script_stats, this, _1, _2)));
  
  wizard.values().gset("sql_script", alter_script);
  wizard.apply_page->apply_sql_script= boost::bind(&SqlEditorForm::apply_object_alter_script, this, _1, obj_editor, log_id);
  wizard.run_modal();
  
  if (wizard.applied() && !wizard.has_errors())
    set_log_message(log_id, DbSqlEditorLog::OKMsg, _("Changes applied"), log_context, "");
  else
    set_log_message(log_id, DbSqlEditorLog::ErrorMsg, errors, log_context, "");
      
  return wizard.applied() && !wizard.has_errors();
}


int SqlEditorForm::sql_script_apply_error(long long code, const std::string& msg, const std::string& stmt, std::string &errors)
{
  if (code >= 0)
    errors.append(strfmt("Error %li: ", (long)code));
  errors.append(msg).append("\n");
  if (!stmt.empty())
    errors.append("SQL Statement:\n").append(stmt).append("\n\n");
  return 0;
}


int SqlEditorForm::sql_script_apply_progress(float)
{
  return 0;
}


int SqlEditorForm::sql_script_stats(long, long)
{
  return 0;
}


void SqlEditorForm::apply_object_alter_script(std::string &alter_script, bec::DBObjectEditorBE* obj_editor, RowId log_id)
{
  set_log_message(log_id, DbSqlEditorLog::BusyMsg, "", 
                  obj_editor ? strfmt(_("Applying changes to %s..."), obj_editor->get_name().c_str()) : _("Applying changes..."), "");
  
  SqlFacade::Ref sql_splitter= SqlFacade::instance_for_rdbms(rdbms());
  std::list<std::string> statements;
  sql_splitter->splitSqlScript(alter_script, statements);
  
  int max_query_size_to_log = _options.get_int("DbSqlEditor:MaxQuerySizeToHistory", 0);
  
  std::list<std::string> failback_statements;
  if (obj_editor)
  {
    // in case of alter script failure:
    // try to restore object since it could had been successfully dropped before the alter script failed
    db_DatabaseObjectRef db_object= obj_editor->get_dbobject();
    std::string original_object_ddl_script= grt::StringRef::cast_from(db_object->customData().get("originalObjectDDL"));
    if (!original_object_ddl_script.empty())
    {
      // reuse the setting schema statement which is the first statement of the alter script
      std::string sql= *statements.begin();
      if ((0 == sql.find("use")) || (0 == sql.find("USE")))
        failback_statements.push_back(sql);
      sql_splitter->splitSqlScript(original_object_ddl_script, failback_statements);
    }
  }
  
  sql::SqlBatchExec sql_batch_exec;
  sql_batch_exec.stop_on_error(true);
  sql_batch_exec.failback_statements(failback_statements);
  
  sql_batch_exec.error_cb(boost::ref(on_sql_script_run_error));
  sql_batch_exec.batch_exec_progress_cb(boost::ref(on_sql_script_run_progress));
  sql_batch_exec.batch_exec_stat_cb(boost::ref(on_sql_script_run_statistics));
  
  /*
  if (obj_editor)
  {
    on_sql_script_run_error.connect(obj_editor->on_live_object_change_error);
    on_sql_script_run_progress.connect(obj_editor->on_live_object_change_progress);
    on_sql_script_run_statistics.connect(obj_editor->on_live_object_change_statistics);
  }*/
  
  long sql_batch_exec_err_count= 0;
  {
    try
    {
      GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
      std::auto_ptr<sql::Statement> stmt(_aux_dbc_conn->ref->createStatement());
      sql_batch_exec_err_count= sql_batch_exec(stmt.get(), statements);
    }
    catch (sql::SQLException &e)
    {
      set_log_message(log_id, DbSqlEditorLog::ErrorMsg, strfmt(SQL_EXCEPTION_MSG_FORMAT, e.getErrorCode(), e.what()), strfmt(_("Apply ALTER script for %s"), obj_editor->get_name().c_str()), "");
    }
    catch (std::exception &e)
    {
      set_log_message(log_id, DbSqlEditorLog::ErrorMsg, strfmt(EXCEPTION_MSG_FORMAT, e.what()), strfmt(_("Apply ALTER script for %s"), obj_editor->get_name().c_str()), "");
    }
  }
    
  if (!max_query_size_to_log || max_query_size_to_log >= (int)alter_script.size() )
    _history->add_entry(sql_batch_exec.sql_log());
  
  // refresh object's state only on success, to not lose changes made by user
  if (obj_editor && (0 == sql_batch_exec_err_count))
  {
    db_DatabaseObjectRef db_object= obj_editor->get_dbobject();
  
    set_log_message(log_id, DbSqlEditorLog::OKMsg, strfmt(_("Changes applied to %s"), obj_editor->get_name().c_str()), "", "");

    // refresh state of created/altered object in physical overview
    {
      std::string schema_name= db_SchemaRef::can_wrap(db_object) ? std::string() : *db_object->owner()->name();
      db_SchemaRef schema;
      if (!schema_name.empty())
        schema= db_SchemaRef::cast_from(db_object->owner());
      
      wb::LiveSchemaTree::ObjectType db_object_type;
      if (db_SchemaRef::can_wrap(db_object))
        db_object_type= wb::LiveSchemaTree::Schema;
      else if (db_TableRef::can_wrap(db_object))
        db_object_type= wb::LiveSchemaTree::Table;
      else if (db_ViewRef::can_wrap(db_object))
        db_object_type= wb::LiveSchemaTree::View;
      else if (db_RoutineRef::can_wrap(db_object))
        db_object_type= wb::LiveSchemaTree::Routine;
      
      refresh_live_object_in_overview(db_object_type, schema_name, db_object->oldName(), db_object->name());
    }
    
    refresh_live_object_in_editor(obj_editor, false);
  }
}


void SqlEditorForm::create_live_table_stubs(bec::DBObjectEditorBE *table_editor)
{
  db_DatabaseObjectRef db_object= table_editor->get_dbobject();
  if (db_object->customData().has_key("isLiveTableListLoaded"))
    return;
  
  try
  {
    GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
    
    db_CatalogRef catalog= table_editor->get_catalog();
    grt::ListRef<db_Schema> schemata= catalog->schemata();
    db_SchemaRef schema;
    grt::ListRef<db_Table> tables;
    db_TableRef table;
    
    std::string database_package= *table_editor->get_rdbms()->databaseObjectPackage();
    std::string schema_typename= database_package + ".Schema";
    std::string table_typename= database_package + ".Table";
    grt::GRT *grt= _grtm->get_grt();
    
    std::string prev_schema_name;
    
    boost::shared_ptr<sql::ResultSet> rs;
    {
      std::string schema_name= db_SchemaRef::cast_from(db_object->owner())->name();
      std::list<sql::SQLString> table_types;
      table_types.push_back("TABLE");
      rs.reset(_aux_dbc_conn->ref->getMetaData()->getTables("", schema_name, "%", table_types));
    }
    while (rs->next())
    {
      std::string schema_name= rs->getString(2);
      std::string table_name= rs->getString(3);
      if (prev_schema_name != schema_name)
      {
        schema= find_named_object_in_list(schemata, schema_name);
        if (!schema.is_valid())
        {
          schema= grt->create_object<db_Schema>(schema_typename);
          schema->owner(catalog);
          schema->name(schema_name);
          schema->oldName(schema_name);
          schema->modelOnly(1);
          schemata.insert(schema);
        }
        tables= schema->tables();
        prev_schema_name= schema_name;
      }
      table= find_named_object_in_list(tables, table_name);
      if (!table.is_valid())
      {
        table= grt->create_object<db_Table>(table_typename);
        table->owner(schema);
        table->name(table_name);
        table->oldName(table_name);
        table->modelOnly(1);
        table->isStub(1);
        tables.insert(table);
      }
    }
    
    db_object->customData().set("isLiveTableListLoaded", IntegerRef(1));
  }
  CATCH_ANY_EXCEPTION_AND_DISPATCH(_("Create live table stub"));
}


bool SqlEditorForm::expand_live_table_stub(bec::DBObjectEditorBE *table_editor, const std::string &schema_name, const std::string &obj_name)
{
  db_CatalogRef catalog= table_editor->get_catalog();
  db_TableRef table;
  db_SchemaRef schema= find_named_object_in_list(catalog->schemata(), schema_name);
  if (schema.is_valid())
  {
    table= find_named_object_in_list(schema->tables(), obj_name);
    if (table.is_valid() && table->customData().has_key("isStubExpanded"))
      return true; // stub table has already been expanded
  }
  
  std::string ddl_script;
  try
  {
    GMutexLock aux_dbc_conn_mutex= ensure_valid_aux_connection();
    
    std::auto_ptr<sql::ResultSet> rs(_aux_dbc_conn->ref->getMetaData()->getSchemaObjects("", schema_name, "table", true, obj_name));
    if (rs->next())
    {
      ddl_script= sqlstring("use !;\n", 0) << schema_name;
      ddl_script+= rs->getString(5);
    }
  }
  catch(const sql::SQLException&) {}
  
  if (ddl_script.empty())
    return false;
  
  {
    SqlFacade::Ref sql_facade= SqlFacade::instance_for_rdbms(table_editor->get_rdbms());
    Sql_parser::Ref sql_parser= sql_facade->sqlParser();
    sql_parser->messages_enabled(false);
    grt::DictRef options(_grtm->get_grt());
    {
      std::string sql_mode;
      if (get_session_variable(_aux_dbc_conn, "sql_mode", sql_mode))
        options.gset("sql_mode", sql_mode);
    }
    sql_parser->parse_sql_script(catalog, ddl_script, options);
  }
  
  // find parsed table
  if (!schema.is_valid())
    schema= find_named_object_in_list(catalog->schemata(), schema_name);
  if (!table.is_valid() && schema.is_valid())
    table= find_named_object_in_list(schema->tables(), obj_name);
  
  if (table.is_valid() && table != table_editor->get_dbobject())
  {
    table->modelOnly(0);
    table->isStub(1);
    table->customData().set("isStubExpanded", IntegerRef(1));
  }
  
  return table.is_valid();
}




bool SqlEditorForm::activate_live_object(GrtObjectRef object)
{
  std::string obj_name= *object->name();
  std::string owner_name= *object->owner()->name();
  
  if (db_SchemaRef::can_wrap(object))
    schema_object_activated("activate", LiveSchemaTree::Schema, "", obj_name);
  else if (db_TableRef::can_wrap(object))
    schema_object_activated("activate", LiveSchemaTree::Table, owner_name, obj_name);
  else if (db_ViewRef::can_wrap(object))
    schema_object_activated("activate", LiveSchemaTree::View, owner_name, obj_name);
  else if (db_RoutineRef::can_wrap(object))
    schema_object_activated("activate", LiveSchemaTree::Routine, owner_name, obj_name);
  else
    return false;
  
  return true;
}


bool SqlEditorForm::create_live_object(GrtObjectRef object_type, std::string owner_name, std::string obj_name)
{
  if (db_SchemaRef::can_wrap(object_type))
    tree_create_object(LiveSchemaTree::Schema, "", "");
  else if (db_TableRef::can_wrap(object_type))
    tree_create_object(LiveSchemaTree::Table, owner_name, "");
  else if (db_ViewRef::can_wrap(object_type))
    tree_create_object(LiveSchemaTree::View, owner_name, "");
  else if (db_RoutineRef::can_wrap(object_type))
    tree_create_object(LiveSchemaTree::Routine, owner_name, "");
  else
    return false;
  
  return true;
}


bool SqlEditorForm::drop_live_object(GrtObjectRef object_type, std::string owner_name, std::string obj_name)
{
  wb::LiveSchemaTree::ChangeRecord record = { LiveSchemaTree::Schema, owner_name, obj_name };
  std::vector<wb::LiveSchemaTree::ChangeRecord> changes;
  
  if (db_TableRef::can_wrap(object_type))
    record.type = LiveSchemaTree::Table;
  else if (db_ViewRef::can_wrap(object_type))
    record.type = LiveSchemaTree::View;
  else if (db_RoutineRef::can_wrap(object_type))
    record.type = LiveSchemaTree::Routine;
  else
    if (!db_SchemaRef::can_wrap(object_type))
      return false;
  
  changes.push_back(record);
  tree_drop_objects(changes);
  
  return true;
}


void SqlEditorForm::refresh_schema_tree(bool hard_refresh)
{
  live_schemata_refresh_task->exec(false,
                                   boost::bind((grt::StringRef(SqlEditorForm::*)(grt::GRT *, Ptr, bool))&SqlEditorForm::do_refresh_schema_tree_safe, this, _1, 
                                               weak_ptr_from(this), hard_refresh));
}


grt::StringRef SqlEditorForm::do_refresh_schema_tree_safe(grt::GRT *grt, Ptr self_ptr, bool hard_refresh)
{
  RETVAL_IF_FAIL_TO_RETAIN_WEAK_PTR (SqlEditorForm, self_ptr, self, grt::StringRef(""))
  
  do_refresh_schema_tree(hard_refresh);
  
  return grt::StringRef("");
}


void SqlEditorForm::do_refresh_schema_tree(bool hard_refresh)
{
  if (_is_refreshing_schema_tree)
    return;
  _is_refreshing_schema_tree= true;
  
  std::list<std::string> schema_list;
  if (hard_refresh)
    schema_list = fetch_schema_list();

  live_schemata_refresh_task->execute_in_main_thread(
                                                     boost::bind(
                                                                 &SqlEditorForm::do_refresh_schema_tree_ui, this, 
                                                                 schema_list,
                                                                 hard_refresh),
                                                     false,
                                                     false);
}


void SqlEditorForm::do_refresh_schema_tree_ui(const std::list<std::string> &schema_list, bool hard_refresh)
{
  if (hard_refresh)
    _schema_tree->refresh_ex(schema_list, hard_refresh);
  else
    _schema_tree->refresh_ex(hard_refresh);

  _side_bar->restore_expanded_nodes();
  
  _is_refreshing_schema_tree= false;
}



bec::MenuItemList SqlEditorForm::get_live_catalog_menu_items(const std::string &schema, 
                                                             const std::string &object, 
                                                             const std::string &type)
{
  grt::ListRef<GrtObject> list(_context_ui->get_wb()->get_grt());
  std::list<std::string> groups;
  
  db_query_LiveDBObjectRef dbobject(list.get_grt());
  dbobject->owner(_wbsql->get_grt_editor_object(this));
  dbobject->schemaName(schema);
  dbobject->type(type);
  dbobject->name(object);
  if (type == "schema")
    dbobject->name(schema);
  list.insert(dbobject);
  
  bec::ArgumentPool argument_pool;
  _context_ui->get_wb()->update_plugin_arguments_pool(argument_pool);
  
  argument_pool.add_entries_for_object("", dbobject, "");
  argument_pool.add_entries_for_object(type, dbobject, "");
  
  groups.push_back("Menu/SQL/Catalog");
  
  return grt_manager()->get_plugin_context_menu_items(groups, argument_pool);
}


bool SqlEditorForm::call_live_catalog_menu_item(const std::string &item,
                                                const std::string &schema, const std::string &object, 
                                                const std::string &type)
{  
  db_query_LiveDBObjectRef dbobject(_context_ui->get_wb()->get_grt());
  dbobject->owner(_wbsql->get_grt_editor_object(this));
  dbobject->schemaName(schema);
  dbobject->type(type);
  dbobject->name(object);
  if (type == "schema")
    dbobject->name(schema);
  
  bec::ArgumentPool argument_pool;
  _context_ui->get_wb()->update_plugin_arguments_pool(argument_pool);
  
  argument_pool.add_entries_for_object("", dbobject, "");
  argument_pool.add_entries_for_object(type, dbobject, "");
  
  _context_ui->get_command_ui()->activate_command(item, argument_pool);
  return true;
}

