
#include "stdafx.h"

#ifndef _WIN32
#include <cctype>
#include <algorithm>
#include <sstream>
#endif

#include "db_mysql_statement_parser_be.h"
#include "db_mysql_statement_parser_be_utils.h"
#include "grt/grt_manager.h"
#include "grt/grtpp_undo_manager.h"
#include "grtpp_util.h"
#include "myx_sql_tree_item.h"
#include "grts/structs.db.mysql.h"
#include "myx_util_functions.h"
#include "myx_statement_parser.h"

#define NEUTRAL_STATE_KEEPER Neutral_state_keeper _nsk(this);
#define SCHEMA_KEEPER Schema_keeper _sk(this);

#define THROW_MSG_WRAPPER(call, text) \
  try \
  { \
    call; \
  } \
  catch (const Parse_exception& e) \
  { \
    std::string err_text= text; \
    throw Parse_exception(err_text); \
  }

#define add_log_message(x, y) do {} while(0);

// in general, this could also include stripping of leading/trailing comments
static std::string strip_sql_definition(const std::string& str)
{
  int start_idx= 0;
  for(std::string::const_iterator e= str.end(), it= str.begin(); it != e; it++, start_idx++)
  {
    char c= *it;
    if((c != ' ') && (c != '\t') && (c != '\r') && (c != '\n'))
      break;
  }

  int npos= str.length() - start_idx;
  for(std::string::const_reverse_iterator e= str.rend(), it= str.rbegin(); it != e; it++, npos--)
  {
    char c= *it;
    if((c != ' ') && (c != '\t') && (c != '\r') && (c != '\n'))
      break;
  }
  return str.substr(start_idx, npos);
}

template<typename T1, typename T2>
void do_transactable_list_insert(UndoManager *undo_man, ListRef<T1> &container, T2 &obj)
{
  if (undo_man)
    undo_man->add_undo(new UndoListAddAction(container));
  container.insert(obj);
}


template<typename T1, typename T2>
void do_transactable_list_remove(UndoManager *undo_man, ListRef<T1> &container, T2 &obj)
{
  if (undo_man)
    undo_man->add_undo(new UndoListRemoveAction(container, obj));
  container.remove_value(obj);
}

template<typename T1>
void do_transactable_list_remove(UndoManager *undo_man, ListRef<T1> &container, size_t index)
{
  if (undo_man)
    undo_man->add_undo(new UndoListRemoveAction(container, index));
  container.remove(index);
}


std::string qualify_obj_name(const char *obj_name, db_mysql_SchemaRef &schema)
{
  std::string norm_schema_name= schema->name();
  std::string norm_obj_name= obj_name;
  std::transform(norm_schema_name.begin(), norm_schema_name.end(), norm_schema_name.begin(), &std::tolower);
  std::transform(norm_obj_name.begin(), norm_obj_name.end(), norm_obj_name.begin(), &std::tolower);

  std::string qualified_obj_name;
  qualified_obj_name
    .append("`")
    .append(norm_schema_name)
    .append("`.`")
    .append(norm_obj_name)
    .append("`");
  return qualified_obj_name;
}


Db_mysql_statement_parser_be::Db_mysql_statement_parser_be()
  : _grt(NULL), _splitter(NULL), _sql(NULL), _processed_obj_count(0), _warn_count(0), _err_count(0), _progress_state(0)
  , _scope(Catalog)
  , _stub_num(0)
  , _treat_error_as_object(false)
{}


Db_mysql_statement_parser_be::  Action Db_mysql_statement_parser_be::get_action()
{
  if (_treat_error_as_object)
    return EImportTreatErrorAsObject;
  return _catalog.is_valid()? EImportGrtObjectRef: ECheckSyntax;
}


Db_mysql_statement_parser_be::Scope Db_mysql_statement_parser_be::get_scope()
{
  return _catalog.is_valid()? Catalog : _scope;
}

void Db_mysql_statement_parser_be::set_mode(GRT *grt, Db_mysql_statement_parser_be::Scope scope, Db_mysql_statement_parser_be::Action action)
{
  _grt= grt;
  _scope= scope;
  _stub_num= 0;
  _treat_error_as_object= (action == EImportTreatErrorAsObject);
  if (_treat_error_as_object)
    _processed_items= grt::StringListRef(grt);
}

int Db_mysql_statement_parser_be::check_trigger_syntax(grt::GRT *grt, const char *sql_script_string)
{
  set_mode(grt, Trigger, ECheckSyntax);
  
  return parse_sql(db_mysql_Catalog(), sql_script_string, false);
}


int Db_mysql_statement_parser_be::check_view_syntax(grt::GRT *grt, const char *sql_script_string)
{
  set_mode(grt, View, ECheckSyntax);
  
  return parse_sql(db_mysql_Catalog(), sql_script_string, false);
}


int Db_mysql_statement_parser_be::check_routine_syntax(grt::GRT *grt, const char *sql_script_string)
{
  set_mode(grt, Routine, ECheckSyntax);
  
  return parse_sql(db_mysql_Catalog(), sql_script_string, false);
}


int Db_mysql_statement_parser_be::parse_inserts(db_mysql_TableRef &table, const char *sql)
{
  _table= table;
  set_mode(table.get_grt(), Insert, EImportTreatErrorAsObject);
  
  int res= parse_sql(db_mysql_Catalog(), sql, false);

  _table= db_mysql_Table();
  _processed_items= grt::StringListRef();

  return res;
}


int Db_mysql_statement_parser_be::parse_triggers(db_mysql_TableRef &table, const char *sql)
{
// todo remove those triggers which are no more in the table
  _table= table;
  set_mode(table.get_grt(), Trigger, EImportTreatErrorAsObject);

  int res= parse_sql(db_mysql_Catalog(), sql, false);

  // remove some triggers from table (those which not specified in _processed_items)
  for(size_t i= 0; i < _table.triggers().count();)
  {
    db_TriggerRef trigger= _table.triggers().get(i);
    for(size_t j= 0, count= _processed_items.count(); j < count && trigger.is_valid(); j++)
      if (*_processed_items.get(j) == *trigger->name())
        trigger= db_Trigger(); // invalidate it - will signal that no removal needed

    if (!trigger.is_valid()) 
      i++;
    else // means trigger is no longer in this table
    {
      do_transactable_list_remove(_undo_man, _table.triggers(), i);
    }
  }

  _table= db_mysql_Table();
  _processed_items= grt::StringListRef();
  return res;
}


int Db_mysql_statement_parser_be::parse_routine(db_mysql_RoutineRef &routine, const char *sql)
{
  _routine= routine;
  set_mode(routine.get_grt(), Routine, EImportTreatErrorAsObject);
  int res= parse_sql(db_mysql_Catalog(), sql, false);
  _routine= db_mysql_Routine();
  _processed_items= grt::StringListRef();
  return res;
}


int Db_mysql_statement_parser_be::parse_routines(db_mysql_RoutineGroupRef &routineGroup, const char *sql)
{
  _routineGroup= routineGroup;
  set_mode(routineGroup.get_grt(), RoutineGroup, EImportTreatErrorAsObject);

  int res= parse_sql(db_mysql_Catalog(), sql, false);

  // remove some routines from group (those which not specified in _processed_items)
  for(size_t i= 0; i < _routineGroup.routines().count();)
  {
    db_RoutineRef routine= _routineGroup.routines().get(i);
    for(size_t j= 0, count= _processed_items.count(); j < count && routine.is_valid(); j++)
      if (*_processed_items.get(j) == *routine->name())
        routine= db_mysql_Routine(); // invalidate it - will signal that no removal needed

    if (!routine.is_valid()) 
      i++;
    else // means routine is no longer in this routineGroup
    {
      if (_routineGroup.routines().get(i).routineType() == std::string("<stub>"))
        do_transactable_list_remove(_undo_man, db_SchemaRef::cast_from(_routineGroup->owner()).routines(), _routineGroup.routines().get(i));
      do_transactable_list_remove(_undo_man, _routineGroup.routines(), i);
    }
  }
  _processed_items= grt::StringListRef();
  _routineGroup= db_mysql_RoutineGroup();
  return res;
}


int Db_mysql_statement_parser_be::parse_sql_script_string(db_mysql_CatalogRef &catalog, const char *sql_script_string)
{
  set_mode(catalog.get_grt(), Catalog, EImportGrtObjectRef);
  return parse_sql(catalog, sql_script_string, false);
}


int Db_mysql_statement_parser_be::parse_sql_script_file(db_mysql_CatalogRef &catalog, const char *sql_script_file)
{
  set_mode(catalog.get_grt(), Catalog, EImportGrtObjectRef);
  return parse_sql(catalog, sql_script_file, true);
}


int Db_mysql_statement_parser_be::parse_sql(db_mysql_CatalogRef &catalog, const char *sql, bool from_file)
{
  NEUTRAL_STATE_KEEPER

  _catalog= catalog;
  if (_catalog.is_valid())
    _grt= _catalog.get_grt();

  add_log_message("Started parsing SQL script.", 0);
  _grt->send_progress(0.0f, _("Parsing MySQL SQL Script..."));

  _undo_man= GRTManager::get_instance_for(_grt)->get_undo_manager();
  if (_undo_man)
    _undo_man->begin_undo_group();

  build_datatype_cache();

  // change current schema to default, it will be used for objects without specified schema
  db_mysql_SchemaRef default_schema;
  int initial_schemata_count= -1;
  Db_mysql_statement_parser_fe parser_fe;

  if (get_action() != EImportGrtObjectRef) 
    parser_fe.ignore_dml= false;
  else
  {
    initial_schemata_count= _catalog.schemata().count();
    if (initial_schemata_count != 0)
    {
      default_schema= _catalog.schemata().get(0);
      set_active_schema((*default_schema->name()).c_str());
    }
    else
      default_schema= set_active_schema("mydb"); // this will add schema to schemata if necessary
  }

  if (from_file)
    _err_count= parser_fe.parse_sql_script_file(sql, &process_sql_statement_cb, this);
  else
    _err_count= parser_fe.parse_sql_script_string(sql, &process_sql_statement_cb, this);

  _grt->send_progress(0.9f, _("Creating Foreign Key References..."));

  // 2-nd stage: now, when all tables are created it's safe to set foreign key references
  set_fkey_references();

  // remove default schema if it was created by this procedure & after all it's empty
  if (_catalog.is_valid()
      && initial_schemata_count == 0
      && default_schema.tables().count() == 0
      && default_schema.views().count() == 0
      && default_schema.routines().count() == 0)
    do_transactable_list_remove(_undo_man, _catalog.schemata(), default_schema);

  if (_undo_man)
  {
    _undo_man->end_undo_group();
    _undo_man->set_action_description("Parse SQL script");
  }

  _grt->send_progress(1.0f, _("Finished Script Reverse Engineering."));
  {
    std::ostringstream oss;
    oss << "Finished parsing SQL script. Totally processed statements: successful (" << _processed_obj_count <<
      "), errors (" << _err_count <<
       "), warnings (" << _warn_count << ").";
    add_log_message(oss.str(), 0);
  }

//!  if(_err_count > 0)
//!    add_log_message("Syntax error", 2);

  step_progress("", true);

  return 1; // success
}


int Db_mysql_statement_parser_be::process_sql_statement_cb(void *parser_be_ptr, const MyxStatementParser *splitter, const char* statement, MyxSQLTreeItem *tree, int lineno, const std::string &err_msg)
{
  Db_mysql_statement_parser_be *parser_be= reinterpret_cast <Db_mysql_statement_parser_be *> (parser_be_ptr);
  return parser_be->process_sql_statement(splitter, statement, tree, lineno, err_msg);
}


int Db_mysql_statement_parser_be::process_sql_statement(const MyxStatementParser *splitter, const char* sql, MyxSQLTreeItem *tree, int lineno, const std::string &err_msg)
{
  _sql= sql;
  _splitter= splitter;
  _lineno= lineno;
  _err_msg= err_msg;

  if (!tree && get_action() != EImportTreatErrorAsObject)
  {
    log_syntax_error(lineno, true, err_msg, 2);
    return -1;
  }

  try
  {
    // count only 'create' statements (for clarity)
    if (pr_processed == convert_parser_dom_to_grt_dom(tree, lineno, err_msg)
        && (!tree || tree->get_subitem_by_path("statement/create")))
      ++_processed_obj_count;
  }
  catch(Parse_exception& e)
  {
    std::string msg_text= e.what();
    msg_text
      .append("\nsql query:\n")
      .append(_sql);
    add_log_message(msg_text, e.flag());
    ++_err_count;
  }

  return 0; // success
}


int Db_mysql_statement_parser_be::rename_schema_references(db_mysql_CatalogRef &catalog, const std::string &old_schema_name, const std::string &new_schema_name)
{
  NEUTRAL_STATE_KEEPER

  _catalog= catalog;
  _grt= _catalog.get_grt();
  _old_schema_name= old_schema_name;
  _new_schema_name= new_schema_name;

  Db_mysql_statement_parser_fe parser_fe;
  
  add_log_message("Started sync of new schema name with SQL statements.", 0);

  _undo_man= GRTManager::get_instance_for(_grt)->get_undo_manager();
  if (_undo_man)
    _undo_man->begin_undo_group();

  ListRef<db_mysql_Schema> schemata= _catalog.schemata();
  for (size_t n= 0, count= schemata.count(); n < count; ++n)
  {
    _active_schema= schemata.get(n);

    // views
    rename_schema_references<db_mysql_View>(
      _active_schema.views(),
      &db_mysql_View::sqlDefinition,
      &db_mysql_View::sqlDefinition,
      0,
      parser_fe);

    // routines
    rename_schema_references<db_mysql_Routine>(
      _active_schema.routines(),
      &db_mysql_Routine::sqlDefinition,
      &db_mysql_Routine::sqlDefinition,
      1,
      parser_fe);

    // tables + triggers
    {
      grt::ListRef<db_mysql_Table> tables= _active_schema.tables();

      // tables
      for (size_t n= 0, count= tables.count(); n < count; ++n)
      {
        // triggers
        rename_schema_references<db_mysql_Trigger>(
          tables.get(n).triggers(),
          &db_mysql_Trigger::sqlDefinition,
          &db_mysql_Trigger::sqlDefinition,
          1,
          parser_fe);
      }
    }
  }

  if (_undo_man)
  {
    _undo_man->end_undo_group();
    _undo_man->set_action_description("Update schema name occurences in existing SQL statements.");
  }

  {
    std::ostringstream oss;
    oss << "Finished sync of new schema name with SQL statements. Totally processed statements: successful (" << _processed_obj_count <<
      "), errors (" << _err_count <<
       "), warnings (" << _warn_count << ").";
    add_log_message(oss.str(), 0);
  }

  return 1; // success
}


template<typename T>
void Db_mysql_statement_parser_be::rename_schema_references(grt::ListRef<T> obj_list,
                                                             grt::StringRef (T::*sql_text_prop_r)() const,
                                                             void (T::*sql_text_prop_w)(const grt::StringRef&),
                                                             int delim_wraping,
                                                             Db_mysql_statement_parser_fe& parser_fe)
{
  for (size_t n= 0, count= obj_list.count(); n < count; ++n)
  {
    T db_obj= obj_list.get(n);
    std::string sql_text= (db_obj.*sql_text_prop_r)();
    if (!sql_text.empty())
    {
      static const std::string begin_delim1= "DELIMITER //\n";
      static const std::string begin_delim2= "DELIMITER //\nCREATE PROCEDURE proc()\n";
      static const std::string end_delim= "\n//\nDELIMITER ;\n;\n";

      std::string begin_delim;

      switch (delim_wraping)
      {
      case 1: begin_delim= begin_delim1; break;
      case 2: begin_delim= begin_delim2; break;
      }

      if (delim_wraping)
      {
        sql_text.reserve(sql_text.size() + begin_delim.size() + end_delim.size());
        sql_text.insert(0, begin_delim).append(end_delim);
      }

      _active_obj= db_obj;
      _err_count+= parser_fe.parse_sql_script_string(sql_text.c_str(), &rename_schema_references_cb, this);
      _active_obj= db_DatabaseObject();

      if (rename_schema_references(sql_text))
      {
        {
          std::string log_msg;
          log_msg
            .append(db_obj.get_attribute("caption"))
            .append(" ")
            .append(db_obj->name())
            .append(" updated with regard to new schema name.");
          ++_processed_obj_count;
          add_log_message(log_msg, 0);
        }

        if (delim_wraping)
        {
          sql_text.erase(sql_text.size() - end_delim.size(), end_delim.size());
          sql_text.erase(0, begin_delim.size());
        }

        if (_undo_man)
          _undo_man->add_undo(new UndoObjectChangeAction(db_objRef, "sqlDefinition")); //! maybe not "sqlDefinition" but some other member
        (db_obj.*sql_text_prop_w)(sql_text);
      }
    }
  }
}


int Db_mysql_statement_parser_be::rename_schema_references_cb(void *parser_be_ptr, const MyxStatementParser *splitter, const char* statement, MyxSQLTreeItem *tree, int lineno, const std::string &err_msg)
{
  Db_mysql_statement_parser_be *parser_be= reinterpret_cast <Db_mysql_statement_parser_be *> (parser_be_ptr);
  parser_be->_splitter= splitter;
  return parser_be->rename_schema_references(splitter, statement, tree, lineno, err_msg);
}


int Db_mysql_statement_parser_be::rename_schema_references(const MyxStatementParser *splitter, const char* sql, const MyxSQLTreeItem *item, int lineno, const std::string &err_msg)
{
  _sql= sql;
  _splitter= splitter;

  // on parsing error log error message
  if (!item)
  {
    log_syntax_error(lineno, true, err_msg, 2);
    return -1;
  }

  {
    // try all rules with dot notation
    const char *name= item->get_name();

    if (are_strings_eq_ci(name, "sp_name"))
      process_schema_reference_candidate(item, 1);

    // else if (are_strings_eq_ci(name, "opt_component"))
      // irrelevant (doesn't refer schema)

    else if (are_strings_eq_ci(name, "function_call_generic"))
      process_schema_reference_candidate(item, 1);

    else if (are_strings_eq_ci(name, "table_wild_one"))
      process_schema_reference_candidate(item, 1);

    // else if (are_strings_eq_ci(name, "opt_wild"))
      // irrelevant (doesn't refer schema)

    else if (are_strings_eq_ci(name, "table_wild"))
      process_schema_reference_candidate(item, 2);

    else if (are_strings_eq_ci(name, "simple_ident_q"))
      process_schema_reference_candidate(item, 2);

    else if (are_strings_eq_ci(name, "field_ident"))
      process_schema_reference_candidate(item, 2);

    else if (are_strings_eq_ci(name, "table_ident"))
      process_schema_reference_candidate(item, 1);

    // else if (are_strings_eq_ci(name, "internal_variable_name"))
      // irrelevant (doesn't refer schema)

    else if (are_strings_eq_ci(name, "grant_ident"))
      process_schema_reference_candidate(item, 1);
  }

  {
    const MyxSQLTreeItem::SubItemList *subitems= item->get_subitems();
    MyxSQLTreeItem::SubItemList::const_iterator it= subitems->begin();
    MyxSQLTreeItem::SubItemList::const_iterator it_end= subitems->end();
    for (; it != it_end; ++it)
    {
      const MyxSQLTreeItem *subitem= *it;
      if (subitem->get_subitems()->size())
        rename_schema_references(splitter, sql, subitem, lineno, err_msg);
    }
  }

  return 0; // success
}


void Db_mysql_statement_parser_be::process_schema_reference_candidate(const MyxSQLTreeItem *item, int dot_count)
{
  const MyxSQLTreeItem *subitem= NULL;
  switch (dot_count)
  {
  case 1:
    subitem= item->check_words("ident", ".");
    break;
  case 2:
    subitem= item->check_words("ident", ".", "ident", ".");
    break;
  }
  if (subitem
      && (subitem= item->check_word("ident"))
      && are_strings_eq_ci(subitem->get_value(), _old_schema_name.c_str()))
    _schema_names_offsets.push_back(_splitter->statement_boffset() + subitem->get_stmt_boffset());
}


bool Db_mysql_statement_parser_be::rename_schema_references(std::string &sql_text)
{
  if (!_schema_names_offsets.empty())
  {
    if (_old_schema_name.size() < _new_schema_name.size())
      sql_text.reserve(sql_text.size() + (_new_schema_name.size() - _old_schema_name.size()) * _schema_names_offsets.size());
    for (std::list<int>::const_reverse_iterator i= _schema_names_offsets.rbegin(); i != _schema_names_offsets.rend(); ++i)
      sql_text.replace(*i, _old_schema_name.size(), _new_schema_name);
    _schema_names_offsets.clear();
    return true;
  }
  return false;
}


Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::convert_parser_dom_to_grt_dom(const MyxSQLTreeItem *parser_dom_tree, int lineno, const std::string &err_msg)
{
  typedef Process_result (Db_mysql_statement_parser_be::*statement_processor)(const MyxSQLTreeItem *);
  static statement_processor proc_arr[]=
  {
    &Db_mysql_statement_parser_be::process_create_table_statement,
    &Db_mysql_statement_parser_be::process_create_index_statement,
    &Db_mysql_statement_parser_be::process_create_view_statement,
    &Db_mysql_statement_parser_be::process_create_routine_statement,
    &Db_mysql_statement_parser_be::process_create_trigger_statement,
    &Db_mysql_statement_parser_be::process_create_server_link_statement,
    &Db_mysql_statement_parser_be::process_create_tablespace_statement,
    &Db_mysql_statement_parser_be::process_create_logfile_group_statement,
    &Db_mysql_statement_parser_be::process_create_schema_statement,
  };

  Process_result result= pr_irrelevant_statement;

  const MyxSQLTreeItem *tree= parser_dom_tree;

  const MyxSQLTreeItem *item= NULL;

  if (tree && (item= tree->get_subitem_by_path("statement/use")))
  {
    result= process_use_schema_statement(item);
  }
  else
  {
    item= tree ? tree->get_subitem_by_path("statement/create") : NULL;

    switch (get_scope())
    {
      case Trigger:
        return process_create_trigger_statement(item);
      case View:
        return process_create_view_statement(item);
      case Routine:
      case RoutineGroup:
        return process_create_routine_statement(item);
      case Insert:
        return process_insert_statement(item);
    }

    if (item)
    {
      for (size_t n= 0; n < ARR_CAPACITY(proc_arr); ++n)
      {
        statement_processor proc= proc_arr[n];
        result= (this->*proc)(item);
        if (pr_irrelevant_statement != result)
          break;
      }
    }
  }
  //else
  //{
  //  log_syntax_error(lineno, true, err_msg, 2);
  //  result= pr_invalid_statement;
  //}

  return result;
}


std::string Db_mysql_statement_parser_be::sql()
{
  std::string sql= _sql;
  //!sql.append("\n").append(_splitter->delimiter());
  //!delimiter shouldn't be stored here, because it's a subject for reparsing
  return sql;
}


void Db_mysql_statement_parser_be::build_datatype_cache()
{
  if (get_action() != EImportGrtObjectRef)
    return;

  _datatype_cache= DictRef(_grt);
  ListRef<db_SimpleDatatype> datatypes= _catalog.simpleDatatypes();
  db_SimpleDatatypeRef datatype;
  for (size_t n= 0; n < datatypes.count(); n++)
  {
    datatype= datatypes.get(n);
    _datatype_cache.set(datatype->name(), datatype);
  }
}


void Db_mysql_statement_parser_be::step_progress(const std::string &text, bool finish)
{
  static std::vector<std::string> details(1);
  //!cycling progress state for now. statement count estimation is needed.
  if (finish)
    _progress_state= 1.0f;
  else
    _progress_state= (float)(div((int)(_progress_state*10) + 1, 10).rem) / 10;

  details[0]= text;
  _grt->send_progress(_progress_state, _("Processing object"), details);
}


void Db_mysql_statement_parser_be::log_db_obj_created(const GrtNamedObjectRef *obj1, const GrtNamedObjectRef *obj2, const GrtNamedObjectRef *obj3)
{
  std::string text;
  text
    .append("Created ")
    .append((obj3?obj3:(obj2?obj2:obj1))->get_attribute("caption"))
    .append(": ");
  if (obj1) text.append(obj1->name());
  if (obj2) text.append(".").append(obj2->name());
  if (obj3) text.append(".").append(obj3->name());
  add_log_message(text, 0);
}


void Db_mysql_statement_parser_be::log_syntax_error(int lineno, bool calc_abs_lineno, const std::string &err_msg, int entry_type, std::string resolution)
{
  if (calc_abs_lineno)
  {
    // count statement line count
    int stmt_lc= 0;
    {
      //! this count ignores mb chars, so false recognition of '\n' can appear
      for (const char *c= _sql; *c != '\0'; ++c)
        if ('\n' == *c)
          ++stmt_lc;
    }

    lineno= _splitter->total_line_count() - stmt_lc + lineno;
  }

  std::ostringstream oss;
  if (_active_obj.is_valid())
    oss << _active_obj.get_attribute("caption") << " " << *_active_obj->name() << ". ";
  oss << "Line " << lineno << ": " << err_msg << ". " << resolution;
  add_log_message(oss.str(), entry_type);
}

#ifndef add_log_message
void Db_mysql_statement_parser_be::add_log_message(const std::string &text, int entry_type)
{
  if (get_action() == ECheckSyntax)
    return;

  switch (entry_type)
  {
  case 0:
    {
      _grt->send_info(text);
      break;
    }
  case 1:
    {
      ++_warn_count;
      _grt->send_warning(text);
      break;
    }
  case 2:
    {
      _grt->send_error(text);
      break;
    }
  }
}
#endif

void Db_mysql_statement_parser_be::set_fkey_references()
{
  if (get_action() != EImportGrtObjectRef)
    return;

  grt::ListRef<db_mysql_Schema> schemata= _catalog.schemata();

  for (Foreign_key_ref_collection::iterator i= _fkey_refs.begin(); i != _fkey_refs.end(); ++i)
  {
    // referred table
    db_mysql_SchemaRef ref_schema= find_named_object_in_list_ci(schemata, i->ref_schema_name().c_str());
    db_mysql_TableRef ref_table;
    {
      ref_table= find_named_object_in_list_ci(ref_schema.tables(), i->ref_table_name().c_str());
      if (!ref_table.is_valid())
      {
        {
          std::string msg_text;
          msg_text.append("table `")
            .append(i->ref_schema_name())
            .append("`.`")
            .append(i->ref_table_name())
            .append("` not found. stub was created.");
          add_log_message(msg_text, 1);
        }
        create_stub_table(
          ref_schema,
          ref_table,
          i->ref_table_name().c_str());
      }
      (*i).operator db_ForeignKeyRef &().referedTable(ref_table);
    }

    // ref columns
    for (Foreign_key_ref::String_collection::iterator c= i->ref_column_names().begin();
      c != i->ref_column_names().end(); ++c)
    {
      db_mysql_ColumnRef column= find_named_object_in_list_ci(ref_table.columns(), c->c_str());
      if (!column.is_valid())
      {
        if (ref_table.isStub())
        {
          {
            std::string msg_text;
            msg_text
              .append("column `")
              .append(*ref_schema->name())
              .append("`.`")
              .append(*ref_table->name())
              .append("`.`")
              .append(*c)
              .append("` not found. stub was created.");
            add_log_message(msg_text, 1);
          }
          create_stub_column(ref_table,
            column,
            c->c_str());
        }
        else
        {
          std::string err_text;
          err_text
            .append("referred column `")
            .append(*ref_schema->name())
            .append("`.`")
            .append(*ref_table->name())
            .append("`.`")
            .append(*c)
            .append("` not found");
          throw Parse_exception(err_text);
        }
      }
      else
        i->operator db_ForeignKeyRef &().referedColumns().insert(column);
    }
  }
}


db_mysql_SchemaRef Db_mysql_statement_parser_be::set_active_schema(const char *schema_name)
{
  return _active_schema= ensure_schema_created(schema_name);
}


db_mysql_SchemaRef Db_mysql_statement_parser_be::ensure_schema_created(const char *schema_name)
{
  if (!schema_name || !strlen(schema_name))
    return _active_schema;

  // try to find existing one first
  db_mysql_SchemaRef schema= find_named_object_in_list_ci(_catalog.schemata(), schema_name);
  if (!schema.is_valid()) // create if not found
  {
    schema= db_mysql_Schema(_grt);
    schema.owner(_catalog);

    schema.name(schema_name);
    schema.oldName(schema_name);
    schema.defaultCharacterSetName(_catalog.defaultCharacterSetName());
    schema.defaultCollationName(_catalog.defaultCollationName());

    do_transactable_list_insert(_undo_man, _catalog.schemata(), schema);
  }

  return schema;
}


void Db_mysql_statement_parser_be::create_stub_table(db_mysql_SchemaRef &schema, db_mysql_TableRef &obj, const char *obj_name)
{
  obj= db_mysql_Table(_grt);
  obj.owner(schema);
  obj.isStub(1);
  SET_STR_ATTR_FROM_STR(obj.name, obj_name)
  obj.oldName(obj->name());
  do_transactable_list_insert(_undo_man, schema.tables(), obj);
}


void Db_mysql_statement_parser_be::create_stub_column(db_mysql_TableRef &table, db_mysql_ColumnRef &obj, const char *obj_name)
{
  obj= db_mysql_Column(_grt);
  obj.owner(table);
  SET_STR_ATTR_FROM_STR(obj.name, obj_name)
  obj.oldName(obj->name());
  table.columns().insert(obj);
}


void Db_mysql_statement_parser_be::process_field_type_item(const MyxSQLTreeItem *item, db_mysql_ColumnRef &column)
{
  if (item)
  {
    // datatype
    {
      db_SimpleDatatypeRef datatype= map_datatype(item, _datatype_cache);
      if (!datatype.is_valid())
      {
        std::string sql_text= item->restore_sql_text(_sql);
        std::string msg_text= "mapping failed for datatype `" + sql_text + "`";
        add_log_message(msg_text, 1);
      }
      else
        column.simpleType(datatype);
    }

    // datatypeExplicitParams
    {
      const MyxSQLTreeItem *string_list_item= item->get_subitem_by_path("string_list");
      if (string_list_item)
      {
        std::string sql_text;
          sql_text
            .append("(")
            .append(string_list_item->restore_sql_text(_sql))
            .append(")");
        SET_STR_ATTR_FROM_STR(column.datatypeExplicitParams, sql_text.c_str())
      }
    }

    // length
    {
      static const char *pathes[]=
      {
        "NUM",
        "opt_len/NUM",
      };
      const MyxSQLTreeItem *searched_item= item->search_by_pathes(pathes, ARR_CAPACITY(pathes));
      SET_INT_ATTR_FROM_ITEM(column.length, searched_item)
    }

    // float_options
    {
      const char *scale= NULL;
      const char *precision= NULL;
      process_float_options_item(item->get_subitem_by_path("float_options"), &precision, &scale);
      if (scale)
        SET_INT_ATTR_FROM_STR(column.scale, scale)
      if (precision)
        SET_INT_ATTR_FROM_STR(column.precision, precision)
    }

    // field options
    {
      StringListRef flags(column.flags());
      concatenate_items(item->get_subitem_by_path("field_options/field_opt_list"), flags);
    }

    // charset
    {
      static const char *pathes1[]=
      {
        "opt_binary",
        "opt_binary/opt_bin_charset",
      };
      static const char *pathes2[]=
      {
        "charset_name",
        "ASCII_SYM",
        "BYTE_SYM",
        "UNICODE_SYM",
      };
      const MyxSQLTreeItem *searched_item= item->search_by_pathes(pathes1, ARR_CAPACITY(pathes1));
      if (searched_item)
        searched_item= item->search_by_pathes(pathes2, ARR_CAPACITY(pathes2));
      SET_STR_ATTR_FROM_ITEM(column.characterSetName, searched_item)
    }

    // binary
    {
      static const char *pathes1[]=
      {
        "opt_binary/BINARY",
        "opt_binary/opt_bin_mod/BINARY",
        "opt_bin_mod/BINARY",
      };
      static const char *pathes2[]=
      {
        "opt_binary/charset_name",
        "opt_binary/opt_bin_charset/charset_name",
      };

      const MyxSQLTreeItem *searched_item= item->search_by_pathes(pathes1, ARR_CAPACITY(pathes1));
      if (!searched_item)
        searched_item= item->search_by_pathes(pathes2, ARR_CAPACITY(pathes2));

      if (searched_item && are_strings_eq_ci("BINARY", searched_item->get_value()))
        column.flags().insert(searched_item->get_value());
    }
  }
}


void Db_mysql_statement_parser_be::process_field_attributes_item(const MyxSQLTreeItem *item, db_mysql_ColumnRef &column, db_mysql_TableRef &table)
{
  bool explicitDefaultValue= false;

  if (item)
  {
    // it was decided that defaultValue will also contain 'ON UPDATE CURRENT_TIMESTAMP' clause
    // do not remove this line until change is consistent with both check_words("DEFAULT") and check_words("ON", "UPDATE_SYM")
    for (MyxSQLTreeItem::SubItemList::const_iterator it= item->get_subitems()->begin(); it != item->get_subitems()->end(); ++it)
    {
      const MyxSQLTreeItem *subitem= *it;
      if (subitem->name_equals("attribute"))
      {
        const MyxSQLTreeItem *aux_item;

        if (subitem->get_subitem_by_name("AUTO_INC"))
          column.autoIncrement(1);
        else if (subitem->check_words("not", "NULL_SYM"))
          column.isNotNull(1);
        else if ((aux_item= subitem->check_words("COMMENT_SYM", "TEXT_STRING_sys")))
          SET_STR_ATTR_FROM_ITEM(column.comment, aux_item)
        else if ((aux_item= subitem->check_words("COLLATE_SYM", "collation_name")))
          SET_STR_ATTR_FROM_ITEM(column.collationName, aux_item)
        else if ((aux_item= subitem->check_words("DEFAULT", "now_or_signed_literal")))
        {
          SET_SQL_ATTR_FROM_ITEM(column.defaultValue, aux_item)
          if (aux_item->get_subitem_by_path("signed_literal/literal/NULL_SYM"))
            column.defaultValueIsNull(1);
          explicitDefaultValue= true;
        }
        else if ((aux_item= subitem->check_words("ON", "UPDATE_SYM", "NOW_SYM", "optional_braces")))
        {
          std::string text;
          // it was decided that 'ON UPDATE CURRENT_TIMESTAMP' should be stored in defaultValue to not create new rarely-used field
          if (column.defaultValue().valueptr())
          {
            text= *column.defaultValue();
            text.append(" ");
          }

          text.append(subitem->get_subitem_by_name("ON")->get_value()).append(" ")
            .append(subitem->get_subitem_by_name("UPDATE_SYM")->get_value()).append(" ")
            .append(subitem->get_subitem_by_name("NOW_SYM")->get_value());

          SET_STR_ATTR_FROM_STR(column.defaultValue, text.c_str());
        }
        else if ((aux_item= subitem->get_subitem_by_name("UNIQUE_SYM")) || (aux_item= subitem->get_subitem_by_name("KEY_SYM")))
        {
          db_mysql_IndexRef index(table.get_grt());
          index.owner(table);

          // index type
          SET_SQL_ATTR_FROM_ITEM(index.indexType, aux_item)
          if (are_strings_eq_ci("UNIQUE_SYM", aux_item->get_name()))
            index.unique(1);
          else
          {
            index.isPrimary(1);
            table.primaryKey(index);
          }

          // index columns
          {
            db_mysql_IndexColumnRef index_column(table.get_grt());
            index_column.owner(index);

            // name
            SET_STR_ATTR_FROM_GRTSTR(index_column.name, column->name())

            // column
            index_column.referedColumn(column);

            index.columns().insert(index_column);
          }

          table.indices().insert(index);
        }
      }
    }
  }

  if (!column.isNotNull() && !explicitDefaultValue)
  {
    column.defaultValue("NULL");
    column.defaultValueIsNull(1);
  }
}


const char * Db_mysql_statement_parser_be::process_field_name_item(const MyxSQLTreeItem *item, GrtNamedObjectRef *obj, const char **name3, const char **name2, const char **name1)
{
  const char * name= NULL;

  if (name1)
    *name1= NULL;
  if (name2)
    *name2= NULL;
  if (name3)
    *name3= NULL;

  if (item)
  {
    size_t n= 4;
    for (MyxSQLTreeItem::SubItemList::const_reverse_iterator it= item->get_subitems()->rbegin(), end= item->get_subitems()->rend(); it != end; ++it)
    {
      const MyxSQLTreeItem *subitem= *it;
      if (!are_strings_eq_ci(",", subitem->get_value()))
      {
        switch (--n)
        {
          case 1:
          {
            if (name1)
              *name1= subitem->get_value();
            break;
          }
          case 2:
          {
            if (name2)
              *name2= subitem->get_value();
            break;
          }
          case 3:
          {
            name= subitem->get_value();
            if (name3)
              *name3= name;
            break;
          }
        }
      }
    }

    if (obj)
    {
      SET_STR_ATTR_FROM_STR(obj->name, name)
      SET_STR_ATTR_FROM_STR(obj->oldName, name)
    }
  }

  return name;
}


const char * Db_mysql_statement_parser_be::process_float_options_item(const MyxSQLTreeItem *item, const char **precision, const char **scale)
{
  const char *_precision= NULL;

  if (precision)
    *precision= NULL;
  if (scale)
    *scale= NULL;

  if (item)
  {
    const MyxSQLTreeItem *scale_item= item->get_subitem_by_path("precision");
    if (scale_item)
    {
      const MyxSQLTreeItem *num_item;

      if ((num_item= scale_item->find_word("NUM")))
        _precision= num_item->get_value();

      if (scale &&
          ((num_item= scale_item->find_word("NUM", scale_item->get_subitem_by_path(",")))))
        *scale = num_item->get_value();
    }
    else
    {
      const MyxSQLTreeItem *num_item;
      if (num_item= item->find_word("NUM"))
        _precision= num_item->get_value();
    }
  }

  if (precision)
    *precision= _precision;

  return _precision;
}


const char * Db_mysql_statement_parser_be::process_obj_full_name_item(const MyxSQLTreeItem *item, db_mysql_SchemaRef *schema, GrtNamedObjectRef *obj)
{
  const char * obj_name= NULL;
  const char * schema_name= NULL;

  if (item)
  {
    if (3 == item->get_subitems()->size()) // ident.ident
      schema_name= (*item->get_subitems()->begin())->get_value();
    obj_name= (*item->get_subitems()->rbegin())->get_value();

    if (obj)
    {
      // name
      SET_STR_ATTR_FROM_STR(obj->name, obj_name);
      if (get_action() == EImportGrtObjectRef)
      {
        SET_STR_ATTR_FROM_STR(obj->oldName, obj_name);
      }
    }
  }

  // this will add schema to schemata if necessary
  if (schema && !schema->is_valid())
    *schema= ensure_schema_created(schema_name);
  else if (schema_name && get_action() == EImportGrtObjectRef)
    ensure_schema_created(schema_name);

  return obj_name;
}


void Db_mysql_statement_parser_be::process_index_item(const MyxSQLTreeItem *tree, db_mysql_TableRef &table)
{
  db_mysql_IndexRef obj(_grt);
  obj.owner(table);

  // name
  {
    static const char *pathes[]=
    {
      "opt_ident/field_ident",
      "opt_constraint/constraint/opt_ident/field_ident",
    };
    const MyxSQLTreeItem *item= tree->search_by_pathes(pathes, ARR_CAPACITY(pathes));
    process_field_name_item(item, &obj);
  }
  
  // index type
  {
    static const char *pathes[]=
    {
      "key_type",
      "constraint_key_type",
    };

    const MyxSQLTreeItem *item= tree->search_by_pathes(pathes, ARR_CAPACITY(pathes));
    if (item)
    {
      SET_SQL_ATTR_FROM_ITEM(obj.indexType, item)

      if (item->get_subitem_by_path("PRIMARY_SYM"))
      {
        obj.isPrimary(1);
        table.primaryKey(obj);
        if (obj->name().operator std::string().empty())
        {
          SET_STR_ATTR_FROM_STR(obj.name, "PRIMARY")
          SET_STR_ATTR_FROM_GRTSTR(obj.oldName, obj->name())
        }
      }
      else if (item->get_subitem_by_path("UNIQUE_SYM"))
        obj.unique(1);
    }
    else
      obj.indexType("INDEX");
  }

  // index kind
  SET_STR_ATTR_FROM_SUBITEM(obj.indexKind, tree, "key_alg/key_using_alg/btree_or_rtree")

  // columns
  {
    const MyxSQLTreeItem *items= tree->get_subitem_by_path("key_list");
    if (items)
    {
      db_mysql_IndexColumnRef index_column(_grt);
      for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("key_part"))
        {
          index_column= db_mysql_IndexColumn(_grt);
          index_column.owner(obj);

          // name
          SET_STR_ATTR_FROM_SUBITEM(index_column.name, item, "ident")

          // column
          db_mysql_ColumnRef column= find_named_object_in_list_ci(table.columns(), index_column->name().c_str());
          if (!column.is_valid())
          {
            std::string err_text= "column `" + *index_column->name() + "` not found";
            throw Parse_exception(err_text);
          }
          index_column.referedColumn(column);
          
          // stored function
          // serg: there is nothing relevant in separate 'CREATE' statement (v5.1)
          //index_column.storedFunction("");

          // stored function
          // serg: there is nothing relevant in separate 'CREATE' statement (v5.1)
          //index_column.comment("");

          // length
          SET_INT_ATTR_FROM_SUBITEM(index_column.columnLength, item, "NUM");

          obj.columns().insert(index_column);
        }
        else if (item->name_equals("order_dir"))
        {
          // order direction
          index_column.descend(are_strings_eq_ci("desc", item->get_value()) ? 1 : 0);
        }
      }
    }
  }

  // options
  {
    const MyxSQLTreeItem *items= tree->get_subitem_by_path("key_options/key_opts");
    if (items)
    {
      for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("key_opt"))
        {
          const MyxSQLTreeItem *aux_item;
          if (aux_item= item->check_word("key_using_alg"))
            SET_STR_ATTR_FROM_SUBITEM(obj.indexKind, aux_item, "btree_or_rtree")
          else if (item->check_word("KEY_BLOCK_SIZE"))
            SET_INT_ATTR_FROM_SUBITEM(obj.keyBlockSize, item, "ulong_num")
          else if (item->check_words("WITH", "PARSER_SYM"))
            SET_STR_ATTR_FROM_SUBITEM(obj.withParser, item, "IDENT_sys");
        }
      }
    }
  }

  table.indices().insert(obj);
}


void Db_mysql_statement_parser_be::process_fkey_item(const MyxSQLTreeItem *tree, db_mysql_TableRef &table)
{
  db_mysql_ForeignKeyRef obj(_grt);
  obj.owner(table);
  Foreign_key_ref fkey_ref(obj);

  // name
  {
    static const char *pathes[]=
    {
      "opt_ident/field_ident",
      "opt_constraint/constraint/opt_ident/field_ident",
    };
    const MyxSQLTreeItem *item= tree->search_by_pathes(pathes, ARR_CAPACITY(pathes));
    process_field_name_item(item, &obj);
  }

  // own columns
  {
    const MyxSQLTreeItem *items= tree->get_subitem_by_path("key_list");
    if (items)
    {
      for (MyxSQLTreeItem::SubItemList::const_iterator it = items->get_subitems()->begin(); it != items->get_subitems()->end(); it++)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("key_part"))
        {
          const MyxSQLTreeItem *colname_item= item->get_subitem_by_path("ident");

          if (colname_item)
          {
            const char *colname= colname_item->get_value();

            db_mysql_ColumnRef column= find_named_object_in_list_ci(table.columns(), colname);
            if (!column.is_valid())
            {
              std::string err_text;
              err_text
                .append("column `")
                .append(colname)
                .append("` not found");
              throw Parse_exception(err_text);
            }
            else
              obj.columns().insert(column);
          }
        }
      }
    }
  }

  // cardinality/mandatory
  obj.many(1);
  obj.referedMandatory(1);

  process_fkey_references_item(tree->find_word("references"), obj, fkey_ref);

  table.foreignKeys().insert(obj);
  _fkey_refs.push_back(fkey_ref);
}


void Db_mysql_statement_parser_be::process_fkey_references_item(const MyxSQLTreeItem *tree, db_mysql_ForeignKeyRef &fkey, Foreign_key_ref &fkey_ref)
{
  // tree is a 'references' item
  if (!tree)
    return;

  // referred table - use fkey decorator to store names - set references at next stage
  db_mysql_SchemaRef ref_schema;
  db_mysql_TableRef ref_table;
  {
    const char *ref_obj_name= process_obj_full_name_item(tree->get_subitem_by_path("table_ident"), &ref_schema);
    fkey_ref.ref_schema_name(ref_schema->name());
    SET_STR_ATTR_FROM_STR(fkey_ref.ref_table_name, ref_obj_name)
  }

  // ref columns names - use fkey decorator to store names - set references at next stage
  {
    const MyxSQLTreeItem *items= tree->get_subitem_by_path("opt_ref_list/ref_list");
    if (items)
    {
      for (MyxSQLTreeItem::SubItemList::const_iterator it = items->get_subitems()->begin(); it != items->get_subitems()->end(); it++)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("ident"))
          fkey_ref.ref_column_names().push_back(item->get_value());
      }
    }
  }

  // on update/delete rule
  {
    const MyxSQLTreeItem *items= tree->get_subitem_by_path("opt_ref_list/opt_on_delete/opt_on_delete_list");
    if (items)
    {
      for (MyxSQLTreeItem::SubItemList::const_iterator it = items->get_subitems()->begin(); it != items->get_subitems()->end(); it++)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("opt_on_delete_item"))
        {
          const MyxSQLTreeItem *delete_option_item;
          bool on_delete= false;
          bool on_update= false;

          if (delete_option_item= item->find_words("DELETE_SYM", "delete_option"))
            on_delete= true;
          else if (delete_option_item= item->find_words("UPDATE_SYM", "delete_option"))
            on_update= true;

          if (on_delete || on_update)
          {
            std::string sql_text= delete_option_item->restore_sql_text(_sql);

            if (on_delete)
              fkey.deleteRule(sql_text);
            else if (on_update)
              fkey.updateRule(sql_text);
          }
        }
      }
    }
  }
}


Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::process_create_table_statement(const MyxSQLTreeItem *tree)
{
  const MyxSQLTreeItem *create2_item= tree->get_subitem_by_path("create2");

  // check if statement is relevant
  if (!create2_item)
    return pr_irrelevant_statement;

  SCHEMA_KEEPER

  db_mysql_SchemaRef schema;
  db_mysql_TableRef obj;
  bool reusing_obj_stub= false;

  /*
  try to find previously created stub object with the same name
  this could happen if table was referenced before creation
  */
  {
    const MyxSQLTreeItem *table_ident_item= tree->find_words("TABLE_SYM", "table_ident");
    if (!table_ident_item)
      table_ident_item= tree->find_words("TABLE_SYM", "opt_if_not_exists", "table_ident");
    const char *obj_name= process_obj_full_name_item(table_ident_item, &schema);

    step_progress(obj_name);

    _active_schema= schema;

    obj= find_named_object_in_list_ci(schema.tables(), obj_name);
    if (obj.is_valid())
    {
      obj.isStub(0);
      {
        std::string msg_text;
        reusing_obj_stub= true;
        msg_text
          .append("previously created stub for table `")
          .append(*schema->name())
          .append("`.`")
          .append(obj_name)
          .append("` was found. reusing. to avoid this warning ensure 'create table' statements go before referencing appropriate tables.");
        add_log_message(msg_text, 1);
      }
    }
    else
    {
      // create db obj
      obj= db_mysql_Table(_grt);
      obj.owner(schema);

      // name + schema
      process_obj_full_name_item(table_ident_item, &schema, &obj);
    }
  }

  //! sql
  //! obj.temp_sql(sql());

  // options
  {
    const MyxSQLTreeItem *items= create2_item->get_subitem_by_path("create2a/opt_create_table_options/create_table_options");
    if (items)
    {
      for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("create_table_option"))
        {
          const MyxSQLTreeItem *aux_item= NULL;

          if (item->check_word("ENGINE_SYM") || item->check_word("TYPE_SYM"))
            SET_STR_ATTR_FROM_SUBITEM(obj.tableEngine, item, "storage_engines")
          else if (item->check_word("ROW_FORMAT_SYM"))
            SET_STR_ATTR_FROM_SUBITEM(obj.rowFormat, item, "row_types")
          else if (item->check_word("AUTO_INC"))
            SET_STR_ATTR_FROM_SUBITEM(obj.nextAutoInc, item, "ulonglong_num")
          else if (aux_item= item->check_word("default_charset"))
            SET_STR_ATTR_FROM_SUBITEM(obj.defaultCharacterSetName, aux_item, "charset_name_or_default")
          else if (aux_item= item->check_word("default_collation"))
            SET_STR_ATTR_FROM_SUBITEM(obj.defaultCollationName, aux_item, "collation_name_or_default")
          else if (item->check_word("DELAY_KEY_WRITE_SYM"))
            SET_INT_ATTR_FROM_SUBITEM(obj.delayKeyWrite, item, "ulong_num")
          else if (item->check_word("COMMENT_SYM"))
            SET_STR_ATTR_FROM_SUBITEM(obj.comment, item, "TEXT_STRING_sys")
          else if (item->check_words("DATA_SYM", "DIRECTORY_SYM"))
            SET_STR_ATTR_FROM_SUBITEM(obj.tableDataDir, item, "TEXT_STRING_sys")
          else if (item->check_words("INDEX_SYM", "DIRECTORY_SYM"))
            SET_STR_ATTR_FROM_SUBITEM(obj.tableIndexDir, item, "TEXT_STRING_sys")
          else if (item->check_word("PACK_KEYS_SYM"))
          {
            SET_STR_ATTR_FROM_SUBITEM(obj.packKeys, item, "DEFAULT")
            SET_STR_ATTR_FROM_SUBITEM(obj.packKeys, item, "ulong_num")
          }
          else if (item->check_word("AVG_ROW_LENGTH"))
            SET_STR_ATTR_FROM_SUBITEM(obj.avgRowLength, item, "ulong_num")
          else if (item->check_word("MIN_ROWS"))
            SET_STR_ATTR_FROM_SUBITEM(obj.minRows, item, "ulonglong_num")
          else if (item->check_word("MAX_ROWS"))
            SET_STR_ATTR_FROM_SUBITEM(obj.maxRows, item, "ulonglong_num")
          else if (item->check_word("CHECKSUM_SYM"))
            SET_INT_ATTR_FROM_SUBITEM(obj.checksum, item, "ulong_num")
          else if (item->check_word("INSERT_METHOD"))
            SET_STR_ATTR_FROM_SUBITEM(obj.mergeInsert, item, "merge_insert_types")
          else if (item->check_word("UNION_SYM"))
          {
            /*
              server cuts schema qualification from table names (only for those located within the same schema with table being defined)
              in union list when using 'show create' statement.
              that's why to avoid false differences during merge procedure, all table names are explicitly normalized (fully qualified names only).
            */
            const MyxSQLTreeItem *items= item->get_subitem_by_path("table_list");
            if (items && items->get_subitems())
            {
              std::string table_list;
              for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
              {
                const MyxSQLTreeItem *item= *it;
                if (item->name_equals("table_name"))
                {
                  db_mysql_SchemaRef schema;
                  const char *obj_name= process_obj_full_name_item(item->get_subitem_by_path("table_ident"), &schema);
                  if (!table_list.empty())
                    table_list.append(", ");
                  table_list.append(qualify_obj_name(obj_name, schema));
                }
              }
              if (!table_list.empty())
                obj.mergeUnion(table_list);
            }
          }
        }
      }
    }
  }

  // columns, indeces, foreign keys
  {
    const MyxSQLTreeItem *items= create2_item->get_subitem_by_path("create2a/field_list");
    if (items)
    {
      for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("field_list_item"))
        {
          const MyxSQLTreeItem *aux_item= NULL;

          if (aux_item= item->get_subitem_by_path("column_def"))
          {
            db_mysql_ColumnRef column;

            // field
            if (aux_item= aux_item->get_subitem_by_path("field_spec"))
            {
              bool reusing_column= false;

              // if current table is reusing stub object then try to find existing column with the same name
              if (reusing_obj_stub)
              {
                const char *field_name= process_field_name_item(aux_item->get_subitem_by_path("field_ident"));
                column= find_named_object_in_list_ci(obj.columns(), field_name);
              }

              if (column.is_valid())
                reusing_column= true;
              else
              {
                column= db_mysql_Column(_grt);
                column.owner(obj);
              }

              // name
              process_field_name_item(aux_item->get_subitem_by_path("field_ident"), &column);

              // type
              process_field_type_item(aux_item->get_subitem_by_path("type"), column);

              // attributes
              process_field_attributes_item(aux_item->get_subitem_by_path("opt_attribute/opt_attribute_list"), column, obj);

              if (!reusing_column)
                obj.columns().insert(column);
            }
            
            if (aux_item= item->get_subitem_by_path("column_def/references"))
            {
              db_mysql_ForeignKeyRef fkey(_grt);
              fkey.owner(obj);
              Foreign_key_ref fkey_ref(fkey);

              // own columns
              fkey.columns().insert(column);

              // cardinality/mandatory
              fkey.many(1);
              fkey.referedMandatory(1);

              // references
              process_fkey_references_item(aux_item, fkey, fkey_ref);

              obj.foreignKeys().insert(fkey);
              _fkey_refs.push_back(fkey_ref);
            }
          }
          else if (aux_item= item->get_subitem_by_path("key_def"))
          {
            if (aux_item->find_words("FOREIGN", "KEY_SYM"))
              THROW_MSG_WRAPPER(
                process_fkey_item(aux_item, obj),
                "table `" + *obj->name() + "` " + e.what())
            else if (aux_item->get_subitem_by_path("key_list"))
              THROW_MSG_WRAPPER(
                process_index_item(aux_item, obj),
                "table `" + *obj->name() + "` " + e.what())
          }
        }
      }
    }
  }

  if (!reusing_obj_stub)
  {
    do_transactable_list_insert(_undo_man, schema.tables(), obj);
    if (!obj.isStub())
      log_db_obj_created(&schema, &obj);
  }

  return pr_processed;
}

static std::string fix_name(const MyxSQLTreeItem *sp_name, std::string sql, std::string obj_name, const MyxSQLTreeItem *create_for_replace= NULL)
{
  if (!sp_name)
    return sql;

  const MyxSQLTreeItem *schema_ident= NULL;
  const MyxSQLTreeItem *obj_ident= NULL;
  if (1 < sp_name->get_subitems()->size())
  {
    schema_ident= sp_name->get_subitem_by_name("ident");
    obj_ident= sp_name->find_words(".", "ident");
  }
  else
    obj_ident= sp_name->get_subitem_by_name("ident");

  if (!obj_ident)
    return sql;

  int stmt_boffset= -1;
  int stmt_eoffset= -1;
  if (schema_ident)
    stmt_boffset= schema_ident->get_stmt_boffset();
  else
    stmt_boffset= obj_ident->get_stmt_boffset();
  stmt_eoffset= obj_ident->get_stmt_eoffset();

  // "`" symbol is not part of ident in lexer, so some tricky code here to guess if it was quoted or not.
  if (stmt_boffset > 0 && '`' == sql[stmt_boffset-1])
    --stmt_boffset;
  if (stmt_eoffset > 0 && stmt_eoffset < (int)sql.size() && '`' == sql[stmt_eoffset])
    ++stmt_eoffset;

  sql.replace(
    stmt_boffset,
    stmt_eoffset - stmt_boffset,
    obj_name);

  if (create_for_replace)
  {
    int stmt_boffset1= create_for_replace->get_stmt_boffset();
    int stmt_eoffset1= create_for_replace->get_stmt_eoffset();

    if (stmt_boffset1 > 0 && stmt_eoffset1 > 0
      && stmt_eoffset1 < stmt_boffset )
    {
      sql.replace(
        stmt_boffset1,
        stmt_eoffset1 - stmt_boffset1,
        "CREATE OR REPLACE");
    }
  }
  return sql;
}


Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::process_create_view_statement(const MyxSQLTreeItem *tree)
{
  const MyxSQLTreeItem *view_tail= NULL;
  {
    static const char *pathes[]=
    {
      "view_or_trigger_or_sp_or_event/view_or_trigger_or_sp_or_event_tail/view_tail",
      "view_or_trigger_or_sp_or_event/view_tail",
    };
    view_tail= tree->search_by_pathes(pathes, ARR_CAPACITY(pathes));
  }

  // check if statement is relevant
  if (!view_tail)
    return pr_irrelevant_statement;

  if (get_action() == ECheckSyntax)
    return pr_processed;

  // create db obj
  db_mysql_ViewRef obj(_grt);

  // name + schema
  db_mysql_SchemaRef schema;
  process_obj_full_name_item(view_tail->find_words("VIEW_SYM", "table_ident"), &schema, &obj);
  obj.owner(schema);

  obj.oldServerSqlDefinition(strip_sql_definition(this->sql()));

  bool create_or_replace_exits= !!tree->get_subitem_by_path("view_or_trigger_or_sp_or_event/view_replace_or_algorithm");
  const MyxSQLTreeItem *create_token= create_or_replace_exits? NULL: tree->find_word("CREATE");
  // sql
  std::string sql= fix_name(view_tail->find_words("VIEW_SYM", "table_ident"), this->sql(), qualify_obj_name(obj->name().c_str(), schema), create_token);

  obj.sqlDefinition(strip_sql_definition(sql));
  obj.oldModelSqlDefinition(*obj.sqlDefinition());

  step_progress(obj->name());

  // check option
  if (view_tail->get_subitem_by_path("view_check_option"))
    obj.withCheckCondition(1);

  do_transactable_list_insert(_undo_man, schema.views(), obj);

  log_db_obj_created(&schema, &obj);

  return pr_processed;
}

Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::process_create_routine_statement(const MyxSQLTreeItem *tree)
{
  // create db obj
  db_mysql_RoutineRef obj;
  bool new_routine_created= false;

  if (_routine.is_valid())
    obj= _routine;

  // sql
//  obj.sqlDefinition(strip_sql_definition(sql()));

  // name + schema
  db_mysql_SchemaRef schema;

  if (get_action() == EImportTreatErrorAsObject)
  {
    if (_routineGroup.is_valid())
      schema= db_mysql_SchemaRef::cast_from(_routineGroup->owner());
    else if (_routine.is_valid())
      schema= db_mysql_SchemaRef::cast_from(_routine->owner());

    if (!tree)
    {
      //if (_splitter->delimiter() == ";")
      //{
      //  log_syntax_error(_lineno, true, _err_msg, 2);
      //  return pr_irrelevant_statement;
      //}
      std::string name;
      if (!_routine.is_valid() && _routineGroup.is_valid())
      {
        std::ostringstream ss;
        ss << "routine_" << *_routineGroup->name() << "_" << ++_stub_num << std::endl;
        name= ss.str();
        name.resize(name.size()-1);
        for (size_t i= 0, count= _routineGroup.routines().count(); i < count; i++)
        {
          if (*_routineGroup.routines().get(i)->name() == name)
          {
            obj= db_mysql_RoutineRef::cast_from(_routineGroup.routines().get(i));
            break;
          }
        }
        if (!obj.is_valid())
        {
          new_routine_created= true;
          obj= db_mysql_Routine(_grt);
          do_transactable_list_insert(_undo_man, _routineGroup.routines(), obj);
          obj.routineType("<stub>");
          obj.name(name);
        }
      }
      else
        name= _routine->name();

      if (get_action() == EImportTreatErrorAsObject)
      {
        _processed_items.insert(name.c_str());
      }
      obj.sqlDefinition(strip_sql_definition(sql()));

      log_syntax_error(_lineno, true, _err_msg, 2, "Stub object `"+ name +"` created");
    }
  }
  if (tree)
  {
    const MyxSQLTreeItem *sp_tail= tree->get_subitem_by_path("view_or_trigger_or_sp_or_event/view_or_trigger_or_sp_or_event_tail/sp_tail");

    // check if statement is relevant
    if (!sp_tail)
      return pr_irrelevant_statement;

    if (get_action() != ECheckSyntax)
    {
      // sql
      { //! temporary solution for alter/diff requirement: inject schema name into routine name & wrap both names with '`'
        const MyxSQLTreeItem *sp_name= tree->get_subitem_by_path("view_or_trigger_or_sp_or_event/view_or_trigger_or_sp_or_event_tail/sp_tail/sp_name");
        const char *obj_name= process_obj_full_name_item(sp_name, &schema);
        std::string sql= fix_name(sp_name, this->sql(), qualify_obj_name(obj_name, schema));

        if (get_action() == EImportTreatErrorAsObject)
          _processed_items.insert(obj_name);

        // now time to detect if there is new routine or we should reuse old one
        if (!obj.is_valid() && _routineGroup.is_valid())
        {
          for (size_t i= 0, count= _routineGroup.routines().count(); i < count; i++)
            if (*_routineGroup.routines().get(i)->name() == std::string(obj_name))
            {
              obj= db_mysql_RoutineRef::cast_from(_routineGroup.routines().get(i));
              break;
            }
        }

        // if routine exists add it to routine group (prevent duplication)
        if (!obj.is_valid() && get_scope() == RoutineGroup)
        {
          // now we try to find routine in the schema with the same name and the same body
          for (size_t i= 0, count= schema.routines().count(); i < count; i++)
          {
            if (*schema.routines().get(i)->name() == std::string(obj_name))
            {
              obj= db_mysql_RoutineRef::cast_from(schema.routines().get(i));
              if (_routineGroup.is_valid())
                do_transactable_list_insert(_undo_man, _routineGroup.routines(), (obj));
              break;
            }
          }
        }
        if (!obj.is_valid())
        {
          obj= db_mysql_Routine(_grt);
          if (_routineGroup.is_valid())
            do_transactable_list_insert(_undo_man, _routineGroup.routines(), (obj));
          new_routine_created= true;
        }
        obj.sqlDefinition(strip_sql_definition(sql));
        obj.oldModelSqlDefinition(*obj.sqlDefinition());
        obj.oldServerSqlDefinition(strip_sql_definition(this->sql()));
      }

      process_obj_full_name_item(sp_tail->find_word("sp_name"), &schema, &obj);
      obj.owner(schema);

      step_progress(obj->name());

      const MyxSQLTreeItem *items= NULL;
      const MyxSQLTreeItem *code_item= NULL;

      if (sp_tail->find_word("PROCEDURE"))
      {
        obj.routineType("procedure");
        code_item= sp_tail->find_word("sp_proc_stmt");
        items= sp_tail->get_subitem_by_path("sp_pdparam_list/sp_pdparams");
      }
      else if (sp_tail->find_word("FUNCTION_SYM"))
      {
        obj.routineType("function");
        code_item= sp_tail->get_subitem_by_path("create_function_tail/sp_proc_stmt");
        items= sp_tail->get_subitem_by_path("create_function_tail/sp_fdparam_list/sp_fdparams");

        // return datatype
        SET_SQL_ATTR_FROM_ITEM(obj.returnDatatype, sp_tail->get_subitem_by_path("create_function_tail/type"))
      }

      // params
      if (items)
      {
        for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
        {
          const MyxSQLTreeItem *item= *it;
          if (item->name_equals("sp_pdparam") || item->name_equals("sp_fdparam"))
          {
            db_mysql_RoutineParamRef param(_grt);
            param.owner(obj);

            const MyxSQLTreeItem *aux_item= NULL;

            SET_STR_ATTR_FROM_SUBITEM(param.name, item, "ident")
            SET_STR_ATTR_FROM_SUBITEM(param.paramType, item, "sp_opt_inout")
            
            // datatype
            SET_SQL_ATTR_FROM_ITEM(param.datatype, item->find_word("type"))

            obj.params().insert(param);
          }
        }
      }    
    }
  }

  if (get_action() == ECheckSyntax)
    log_db_obj_created(&obj);
  else
  {
    if (new_routine_created)
    {
      obj.owner(schema);
      do_transactable_list_insert(_undo_man, schema.routines(), obj);
      log_db_obj_created(&schema, &obj);
    }
  }

  return pr_processed;
}

Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::process_create_index_statement(const MyxSQLTreeItem *tree)
{
  // check if statement is relevant
  if (!tree->find_words("INDEX_SYM", "ident"))
    return pr_irrelevant_statement;

  // table
  db_mysql_TableRef table;
  {
    // referenced obj name + schema
    db_mysql_SchemaRef ref_schema;
    const char *ref_obj_name= process_obj_full_name_item(tree->get_subitem_by_path("table_ident"), &ref_schema);

    table= find_named_object_in_list_ci(ref_schema.tables(), ref_obj_name);
    if (!table.is_valid())
    {
      {
        std::string msg_text;
        msg_text
          .append("table `")
          .append(*ref_schema->name())
          .append("`.`")
          .append(ref_obj_name)
          .append("` not found. stub was created.");
        add_log_message(msg_text, 1);
      }
      create_stub_table(ref_schema, table, ref_obj_name);
    }
  }

  // create db obj
  db_mysql_IndexRef obj(_grt);
  obj.owner(table);

  // name
  SET_STR_ATTR_FROM_ITEM(obj.name, tree->find_words("INDEX_SYM", "ident"));
  SET_STR_ATTR_FROM_GRTSTR(obj.oldName, obj->name());

  // is primary
  // serg: primary index can't be created by separate 'CREATE' statement (v5.1)
  //obj.isPrimary(0);

  // deferability
  // serg: there is nothing relevant in separate 'CREATE' statement (v5.1)
  //obj.deferability(0);

  // index type
  {
    const MyxSQLTreeItem *item= tree->get_subitem_by_path("opt_unique_or_fulltext");
    if (item)
    {
      SET_SQL_ATTR_FROM_ITEM(obj.indexType, item)

      if (are_strings_eq_ci("UNIQUE_SYM", item->get_value()))
        obj.unique(1);
    }
    else
      obj.indexType("INDEX");
  }

  // index kind
  SET_STR_ATTR_FROM_SUBITEM(obj.indexKind, tree, "key_alg/key_using_alg/btree_or_rtree")

  // index kind + unique flag
  {
    SET_STR_ATTR_FROM_SUBITEM(obj.indexType, tree, "opt_unique_or_fulltext")
    if (are_strings_eq_ci("unique", obj.indexType().c_str()))
      obj.unique(1);
    SET_STR_ATTR_FROM_SUBITEM(obj.indexType, tree, "opt_unique_or_fulltext")
  }

  // columns
  {
    const MyxSQLTreeItem *items= tree->get_subitem_by_path("key_list");
    if (items)
    {
      db_mysql_IndexColumnRef index_column(_grt);
      for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("key_part"))
        {
          index_column= db_mysql_IndexColumn(_grt);
          index_column.owner(obj);

          // name
          SET_STR_ATTR_FROM_SUBITEM(index_column.name, item, "ident")

          // referred column
          db_mysql_ColumnRef column= find_named_object_in_list_ci(table.columns(), index_column->name().c_str());
          if (!column.is_valid())
          {
            std::string err_text= "column `" + *index_column->name() + "` not found";
            throw Parse_exception(err_text);
          }
          index_column.referedColumn(column);
          
          // stored function
          // serg: there is nothing relevant in separate 'CREATE' statement (v5.1)
          //index_column.storedFunction("");

          // stored function
          // serg: there is nothing relevant in separate 'CREATE' statement (v5.1)
          //index_column.comment("");

          // length
          SET_INT_ATTR_FROM_SUBITEM(index_column.columnLength, item, "NUM");

          obj.columns().insert(index_column);
        }
        else if (item->name_equals("order_dir"))
        {
          // order direction
          index_column.descend(are_strings_eq_ci("desc", item->get_value()) ? 1 : 0);
        }
      }
    }
  }

  // options
  {
    const MyxSQLTreeItem *items= tree->get_subitem_by_path("key_options/key_opts");
    if (items)
    {
      for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("key_opt"))
        {
          const MyxSQLTreeItem *aux_item;
          if (aux_item= item->check_word("key_using_alg"))
            SET_STR_ATTR_FROM_SUBITEM(obj.indexKind, aux_item, "btree_or_rtree")
          else if (item->check_word("KEY_BLOCK_SIZE"))
            SET_INT_ATTR_FROM_SUBITEM(obj.keyBlockSize, item, "ulong_num")
          else if (item->check_words("WITH", "PARSER_SYM"))
            SET_STR_ATTR_FROM_SUBITEM(obj.withParser, item, "IDENT_sys");
        }
      }
    }
  }

  table.indices().insert(obj);

  return pr_processed;
}


Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::process_create_logfile_group_statement(const MyxSQLTreeItem *tree)
{
  // check if statement is relevant
  if (!tree->check_words("CREATE", "LOGFILE_SYM", "GROUP_SYM"))
    return pr_irrelevant_statement;

  // create db obj
  db_mysql_LogFileGroupRef obj(_grt);
  obj.owner(_catalog);

  const MyxSQLTreeItem *info_item= tree->get_subitem_by_path("logfile_group_info");

  // name
  SET_STR_ATTR_FROM_SUBITEM(obj.name, info_item, "logfile_group_name")
  SET_STR_ATTR_FROM_GRTSTR(obj.oldName, obj->name())

  step_progress(obj->name());

  // undofile
  SET_STR_ATTR_FROM_SUBITEM(obj.undoFile,
    info_item, "add_log_file/lg_undofile/TEXT_STRING_sys");

  // options
  {
    const MyxSQLTreeItem *items= info_item->get_subitem_by_path("logfile_group_option_list/logfile_group_options");
    if (items)
    {
      for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("logfile_group_option"))
        {
          const MyxSQLTreeItem *aux_item;
          if (aux_item= item->get_subitem_by_path("opt_ts_initial_size"))
            SET_INT_ATTR_FROM_SUBITEM(obj.initialSize, aux_item, "size_number")
          else if (aux_item= item->get_subitem_by_path("opt_ts_undo_buffer_size"))
            SET_INT_ATTR_FROM_SUBITEM(obj.undoBufferSize, aux_item, "size_number")
          else if (aux_item= item->get_subitem_by_path("opt_ts_engine"))
            SET_STR_ATTR_FROM_SUBITEM(obj.engine, aux_item, "storage_engines")
        }
      }
    }
  }

  do_transactable_list_insert(_undo_man, _catalog.logFileGroups(), obj);

  log_db_obj_created(&obj);

  return pr_processed;
}


Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::process_create_tablespace_statement(const MyxSQLTreeItem *tree)
{
  // check if statement is relevant
  if (!tree->check_words("CREATE", "TABLESPACE"))
    return pr_irrelevant_statement;

  // create db obj
  db_mysql_TablespaceRef obj(_grt);
  obj.owner(_catalog);

  const MyxSQLTreeItem *info_item= tree->get_subitem_by_path("tablespace_info");

  // name
  SET_STR_ATTR_FROM_SUBITEM(obj.name, info_item, "tablespace_name")
  SET_STR_ATTR_FROM_GRTSTR(obj.oldName, obj->name());

  step_progress(obj->name());

  // datafile
  SET_STR_ATTR_FROM_SUBITEM(obj.dataFile,
    info_item, "ts_datafile/TEXT_STRING_sys");

  // logfile group
  {
    const char* logfile_group_name= get_str_attr_from_subitem(info_item, "opt_logfile_group_name/ident");
    db_mysql_LogFileGroupRef logfile_group= find_named_object_in_list_ci(_catalog.logFileGroups(), logfile_group_name);
    if (!logfile_group.is_valid())
    {
      std::string err_text;
      err_text
        .append("log file group `")
        .append(logfile_group_name)
        .append("` not found");
      throw Parse_exception(err_text);
    }
    obj.logFileGroup(logfile_group);
  }

  // options
  {
    const MyxSQLTreeItem *items= info_item->get_subitem_by_path("tablespace_option_list/tablespace_options");
    if (items)
    {
      for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("tablespace_option"))
        {
          const MyxSQLTreeItem *aux_item;
          if (aux_item= item->get_subitem_by_path("opt_ts_initial_size"))
            SET_INT_ATTR_FROM_SUBITEM(obj.initialSize, aux_item, "size_number")
          else if (aux_item= item->get_subitem_by_path("opt_ts_extent_size"))
            SET_INT_ATTR_FROM_SUBITEM(obj.extendSize, aux_item, "size_number")
          else if (aux_item= item->get_subitem_by_path("opt_ts_engine"))
            SET_STR_ATTR_FROM_SUBITEM(obj.engine, aux_item, "storage_engines")
        }
      }
    }
  }

  do_transactable_list_insert(_undo_man, _catalog.tablespaces(), obj);

  log_db_obj_created(&obj);

  return pr_processed;
}


Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::process_create_server_link_statement(const MyxSQLTreeItem *tree)
{
  // check if statement is relevant
  if (!tree->check_words("CREATE", "server_def"))
    return pr_irrelevant_statement;

  // create db obj
  db_mysql_ServerLinkRef obj(_grt);
  obj.owner(_catalog);

  const MyxSQLTreeItem *server_def_item= tree->get_subitem_by_path("server_def");

  // name
  SET_STR_ATTR_FROM_ITEM(obj.name, server_def_item->find_words("SERVER_SYM", "ident_or_text"))
  SET_STR_ATTR_FROM_GRTSTR(obj.oldName, obj->name());

  step_progress(obj->name());

  // foreign data wrapper
  SET_STR_ATTR_FROM_ITEM(obj.wrapperName, server_def_item->find_words("FOREIGN", "DATA_SYM", "WRAPPER_SYM", "ident_or_text"))

  // options
  {
    const MyxSQLTreeItem *items= server_def_item->get_subitem_by_path("server_options_list");
    if (items)
    {
      for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
      {
        const MyxSQLTreeItem *item= *it;
        if (item->name_equals("server_option"))
        {
          if (item->check_word("HOST_SYM"))
            SET_STR_ATTR_FROM_SUBITEM(obj.host, item, "TEXT_STRING_sys")
          else if (item->check_word("DATABASE"))
            SET_STR_ATTR_FROM_SUBITEM(obj.schema, item, "TEXT_STRING_sys")
          else if (item->check_word("USER"))
            SET_STR_ATTR_FROM_SUBITEM(obj.user, item, "TEXT_STRING_sys")
          else if (item->check_word("PASSWORD"))
            SET_STR_ATTR_FROM_SUBITEM(obj.password, item, "TEXT_STRING_sys")
          else if (item->check_word("SOCKET_SYM"))
            SET_STR_ATTR_FROM_SUBITEM(obj.socket, item, "TEXT_STRING_sys")
          else if (item->check_word("OWNER_SYM"))
            SET_STR_ATTR_FROM_SUBITEM(obj.ownerUser, item, "TEXT_STRING_sys")
          else if (item->check_word("PORT_SYM"))
            SET_STR_ATTR_FROM_SUBITEM(obj.port, item, "ulong_num")
        }
      }
    }
  }

  do_transactable_list_insert(_undo_man, _catalog.serverLinks(), obj);

  log_db_obj_created(&obj);

  return pr_processed;
}


Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::process_create_trigger_statement(const MyxSQLTreeItem *tree)
{
  // create db obj
  db_mysql_TriggerRef obj(_grt);

  // sql
  obj.sqlDefinition(strip_sql_definition(sql()));
  obj.oldServerSqlDefinition(strip_sql_definition(sql()));
  // table
  db_mysql_TableRef table;
  
  if (get_action() == EImportTreatErrorAsObject)
  {
    table= _table;
    if (!tree)
    {
      //if (_splitter->delimiter() == ";")
      //{
      //  log_syntax_error(_lineno, true, _err_msg, 2);
      //  return pr_irrelevant_statement;
      //}

      std::ostringstream ss;
      ss << "trigger" << ++_stub_num << std::endl;
      std::string name= ss.str();
      name.resize(name.size()-1);
      obj.name(name);
      log_syntax_error(_lineno, true, _err_msg, 2, "Stub object `"+ name +"` created");
    }
  }
  if (tree)
  {
    const MyxSQLTreeItem *trigger_tail_item= tree->get_subitem_by_path("view_or_trigger_or_sp_or_event/view_or_trigger_or_sp_or_event_tail/trigger_tail");

    // check if statement is relevant
    if (!trigger_tail_item || !trigger_tail_item->check_word("TRIGGER_SYM"))
      return pr_irrelevant_statement;

    if (get_action() != ECheckSyntax)
    {
      // name + schema
      process_obj_full_name_item(trigger_tail_item->get_subitem_by_path("sp_name"), NULL, &obj);

      step_progress(obj->name());
      db_mysql_SchemaRef ref_schema;
      if (table.is_valid())
        ref_schema= db_mysql_SchemaRef::cast_from(table->owner());

      const char *ref_obj_name= process_obj_full_name_item(trigger_tail_item->get_subitem_by_path("table_ident"), &ref_schema);

      if (get_action() == EImportGrtObjectRef)
      {
        table= find_named_object_in_list_ci(ref_schema.tables(), ref_obj_name);
        if (!table.is_valid())
        {
          {
            std::string msg_text;
            msg_text
              .append("table `")
              .append(*ref_schema->name())
              .append("`.`")
              .append(ref_obj_name)
              .append("` not found. stub was created.");
            add_log_message(msg_text, 1);
          }
          create_stub_table(ref_schema, table, ref_obj_name);
        }
      }
      std::string sql= this->sql();
      sql= fix_name(trigger_tail_item->get_subitem_by_path("table_ident"), sql, qualify_obj_name(table->name().c_str(), ref_schema));
      sql= fix_name(trigger_tail_item->get_subitem_by_path("sp_name"), sql, qualify_obj_name(obj->name().c_str(), ref_schema));
      obj.sqlDefinition(strip_sql_definition(sql));

      if (get_action() == EImportGrtObjectRef)
        obj.oldModelSqlDefinition(*obj.sqlDefinition());
      // definer
      SET_SQL_ATTR_FROM_SUBITEM(obj.definer, tree, "view_or_trigger_or_sp_or_event/definer/user");

      // timing
      SET_STR_ATTR_FROM_SUBITEM(obj.timing, trigger_tail_item, "trg_action_time")

      // event
      SET_STR_ATTR_FROM_SUBITEM(obj.event, trigger_tail_item, "trg_event")

      // orientation
      if (trigger_tail_item->find_words("FOR_SYM", "EACH_SYM", "ROW_SYM"))
        SET_STR_ATTR_FROM_STR(obj.orientation, "ROW")

      // enabled
      obj.enabled(1);
    }
  }

  if (get_action() == ECheckSyntax)
    log_db_obj_created(&obj);
  else
  {
    obj.owner(table);
    table.triggers().insert(obj);
    log_db_obj_created(&db_SchemaRef::cast_from(table->owner()), &table, &obj);
    if (_processed_items.is_valid())
      _processed_items.insert(obj->name());
  }

  return pr_processed;
}



Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::process_create_schema_statement(const MyxSQLTreeItem *tree)
{
  // check if statement is relevant
  if (!tree->check_words("CREATE", "DATABASE"))
    return pr_irrelevant_statement;

  const MyxSQLTreeItem *item= tree->find_word("ident");

  if (!item)
    throw Parse_exception("invalid 'create database' statement");

  step_progress(item->get_value());

  db_mysql_SchemaRef schema= ensure_schema_created(item->get_value());
  if (schema.is_valid())
  {
    // options
    {
      const MyxSQLTreeItem *items= tree->get_subitem_by_path("opt_create_database_options/create_database_options");
      if (items)
      {
        for (MyxSQLTreeItem::SubItemList::const_iterator it= items->get_subitems()->begin(); it != items->get_subitems()->end(); ++it)
        {
          const MyxSQLTreeItem *item= *it;
          if (item->name_equals("create_database_option"))
          {
            if (const MyxSQLTreeItem *aux_item= item->get_subitem_by_path("default_charset/charset_name_or_default"))
            {
              std::string value= aux_item->get_value();
              if (!are_strings_eq_ci(value.c_str(), "DEFAULT"))
              {
                value.insert(0, 1, '\'');
                value.append(1, '\'');
              }
              SET_STR_ATTR_FROM_STR(schema.defaultCharacterSetName, value.c_str())
            }
            else if (const MyxSQLTreeItem *aux_item= item->get_subitem_by_path("default_collation/collation_name_or_default"))
            {
              std::string value= aux_item->get_value();
              if (!are_strings_eq_ci(value.c_str(), "DEFAULT"))
              {
                value.insert(0, 1, '\'');
                value.append(1, '\'');
              }
              SET_STR_ATTR_FROM_STR(schema.defaultCollationName, value.c_str())
            }
          }
        }
      }
    }
  }

  log_db_obj_created(&schema);

  return pr_processed;
}


Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::process_use_schema_statement(const MyxSQLTreeItem *tree)
{
  if (get_action() == EImportTreatErrorAsObject)
    return pr_irrelevant_statement;

  // check if statement is relevant
  if (!tree->check_word("USE_SYM"))
    return pr_irrelevant_statement;

  const MyxSQLTreeItem *item= tree->find_word("ident");

  if (!item)
    throw Parse_exception("invalid 'use' statement");

  set_active_schema(item->get_value());

  return pr_processed;
}


Db_mysql_statement_parser_be::Process_result
Db_mysql_statement_parser_be::process_insert_statement(const MyxSQLTreeItem *tree)
{
  return pr_processed;
}
