/* 
 * 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"

// TODO: check if this inclusion is still necessary.
// this doesn't belong here, but there's no other workaround for having access to mysql_info
// because of that we also need to link wbprivate directly to the connector, making their
// dynamic loading pretty much useless
#include <cppconn/../driver/mysql_connection.h>
#include <cppconn/metadata.h>


//#include "grtdb/db_object_helpers.h"

#include "grts/structs.h"
//#include "grts/structs.db.mgmt.h"
#include "grts/structs.db.mysql.h"

#include "grtpp.h"

#include "diff/diffchange.h"
#include "diff_dbobjectmatch.h"
#include <boost/lambda/bind.hpp>
#include <boost/assign/list_of.hpp>

//#define _DB_OBJECT_MATCH_DEBUG_

using namespace grt;

std::string get_qualified_schema_object_name(GrtNamedObjectRef object, const bool case_sensitive)
{
  std::string s("`");
  s.append(object->owner()->name().c_str()).append("`.`").append(object->name().c_str()).append("`");
  return case_sensitive?s:base::toupper(s);
}

std::string get_qualified_schema_object_old_name(GrtNamedObjectRef object, const bool case_sensitive)
{

  const char *parent_name= NULL;
  if (db_mysql_SchemaRef::can_wrap(object->owner()))
  {
      parent_name = db_mysql_SchemaRef::cast_from(object->owner())->name().c_str();
  }
  else if(GrtNamedObjectRef::can_wrap(object->owner()))
  {
    GrtNamedObjectRef pr= GrtNamedObjectRef::cast_from(object->owner());

    parent_name= pr->oldName().empty() ? 
      pr->name().c_str() : pr->oldName().c_str();
  }
  else
  {
    parent_name=
      object->owner()->name().c_str();
  }

  std::string s("`");
  s.append(parent_name).append("`.`").append(object->oldName().c_str()).append("`");

  return case_sensitive?s:base::toupper(s);
}

/////////////////////////////////////////////////////////////////////////////////////////////
// Alter OMF
/////////////////////////////////////////////////////////////////////////////////////////////

bool grt::DbObjectMatchAlterOmf::less(const grt::ValueRef& l, const grt::ValueRef& r) const
{
  if (l.type() == r.type() && l.type() == ObjectType)
  {
    if(db_IndexColumnRef::can_wrap(l) && db_IndexColumnRef::can_wrap(r))
    {
      db_IndexColumnRef lc= db_IndexColumnRef::cast_from(l);
      db_IndexColumnRef rc= db_IndexColumnRef::cast_from(r);
      return less(lc->referencedColumn(), rc->referencedColumn());
    }else if(db_mysql_SchemaRef::can_wrap(l) && db_mysql_SchemaRef::can_wrap(r))
    {
        return strcmp(
            db_mysql_SchemaRef::cast_from(l)->name().c_str(),
            db_mysql_SchemaRef::cast_from(r)->name().c_str()) == 0;
    }else if (GrtNamedObjectRef::can_wrap(l) && GrtNamedObjectRef::can_wrap(r))
    {
      GrtNamedObjectRef left = GrtNamedObjectRef::cast_from(l);
      GrtNamedObjectRef right = GrtNamedObjectRef::cast_from(r);

      if (left.is_valid() && right.is_valid())
      {
        std::string l_str, r_str;

        if(strlen(left->oldName().c_str()) > 0)
          l_str= get_qualified_schema_object_old_name(left, case_sensitive);
        else
          l_str= get_qualified_schema_object_name(left, case_sensitive);

        if(strlen(right->oldName().c_str()) > 0)
          r_str= get_qualified_schema_object_old_name(right, case_sensitive);
        else
          r_str= get_qualified_schema_object_name(right, case_sensitive);

        int result= l_str.compare(r_str);

#ifdef _DB_OBJECT_MATCH_DEBUG_
        std::cout << std::endl << "DbObjectMatchAlterOmf::less(" << l_str << ", " << r_str << ") = " << result << std::endl;
#endif
        return result < 0;
      } 
    }
    else if (GrtObjectRef::can_wrap(l) && GrtObjectRef::can_wrap(r))
    {
      GrtObjectRef left = GrtObjectRef::cast_from(l);
      GrtObjectRef right = GrtObjectRef::cast_from(r);

      if (left.is_valid() && right.is_valid())
      {
        return (strcmp(left->name().c_str(), right->name().c_str()) < 0);
      }
    }
    else if (ObjectRef::can_wrap(l) && ObjectRef::can_wrap(r))
    {
      ObjectRef left = ObjectRef::cast_from(l);
      ObjectRef right = ObjectRef::cast_from(r);
      if ((left.class_name() == right.class_name())
        && left.has_member("oldName"))
      {
        const char *l_str= NULL;
        const char *r_str= NULL;

        if(strlen(left.get_string_member("oldName").c_str()) > 0)
          l_str= left.get_string_member("oldName").c_str();
        else
          l_str= left.get_string_member("name").c_str();

        if(strlen(right.get_string_member("oldName").c_str()) > 0)
          r_str= right.get_string_member("oldName").c_str();
        else
          r_str= right.get_string_member("name").c_str();

        return strcmp(l_str, r_str) < 0;
      }
    }
  }
  return std::less<grt::ValueRef>()(l, r);
}

bool grt::DbObjectMatchAlterOmf::equal(const ValueRef& l, const ValueRef& r) const
{
  if (l.type() == r.type() && l.type() == ObjectType)
  {
    if(db_IndexColumnRef::can_wrap(l) && db_IndexColumnRef::can_wrap(r))
    {
      db_IndexColumnRef lc= db_IndexColumnRef::cast_from(l);
      db_IndexColumnRef rc= db_IndexColumnRef::cast_from(r);
      return equal(lc->referencedColumn(), rc->referencedColumn());
    }else if(db_mysql_SchemaRef::can_wrap(l) && db_mysql_SchemaRef::can_wrap(r))
    {
        return strcmp(
            db_mysql_SchemaRef::cast_from(l)->name().c_str(),
            db_mysql_SchemaRef::cast_from(r)->name().c_str()) == 0;
    }
    else if (GrtNamedObjectRef::can_wrap(l) && GrtNamedObjectRef::can_wrap(r))
    {
      GrtNamedObjectRef left = GrtNamedObjectRef::cast_from(l);
      GrtNamedObjectRef right = GrtNamedObjectRef::cast_from(r);
      
      if (left.is_valid() && right.is_valid())
      {
        std::string l_str, r_str;

        if(strlen(left->oldName().c_str()) > 0)
          l_str= get_qualified_schema_object_old_name(left, case_sensitive);
        else
          l_str= get_qualified_schema_object_name(left, case_sensitive);

        if(strlen(right->oldName().c_str()) > 0)
          r_str= get_qualified_schema_object_old_name(right, case_sensitive);
        else
          r_str= get_qualified_schema_object_name(right, case_sensitive);

        int result= l_str.compare(r_str);

#ifdef _DB_OBJECT_MATCH_DEBUG_
        std::cout << std::endl << "DbObjectMatchAlterOmf::equal(" << l_str << ", " << r_str << ") = " << result << std::endl;
#endif
        return result == 0;
      } 
    }
    else if (GrtObjectRef::can_wrap(l) && GrtObjectRef::can_wrap(r))
    {
      GrtObjectRef left = GrtObjectRef::cast_from(l);
      GrtObjectRef right = GrtObjectRef::cast_from(r);

      if (left.is_valid() && right.is_valid())
      {
        return (strcmp(left->name().c_str(), right->name().c_str()) == 0);
      }
    }
    else if (ObjectRef::can_wrap(l) && ObjectRef::can_wrap(r))
    {
      ObjectRef left = ObjectRef::cast_from(l);
      ObjectRef right = ObjectRef::cast_from(r);
      if (left.is_valid() && right.is_valid()
        && (left.class_name() == right.class_name())
        && left.has_member("oldName"))
      {
        ObjectRef left = ObjectRef::cast_from(l);
        ObjectRef right = ObjectRef::cast_from(r);

        const char *l_str= NULL;
        const char *r_str= NULL;

        if(strlen(left.get_string_member("oldName").c_str()) > 0)
          l_str= left.get_string_member("oldName").c_str();
        else
          l_str= left.get_string_member("name").c_str();

        if(strlen(right.get_string_member("oldName").c_str()) > 0)
          r_str= right.get_string_member("oldName").c_str();
        else
          r_str= right.get_string_member("name").c_str();

        return strcmp(l_str, r_str) == 0;
      }
    }
  }

  return std::equal_to<grt::ValueRef>()(l, r);
}


bool sqlBody_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name, grt::GRT* grt)
{
    std::string sql1= ObjectRef::cast_from(obj1).get_string_member(name);
    std::string sql2= ObjectRef::cast_from(obj2).get_string_member(name);
    SqlFacade* parser = SqlFacade::instance_for_rdbms_name(grt, "Mysql");

    if (!parser)
        return false;
    std::string schema1 = db_TriggerRef::can_wrap(obj1)?
        GrtObjectRef::cast_from(obj1)->owner()->owner()->name():
        GrtObjectRef::cast_from(obj1)->owner()->name();
    std::string schema2 = db_TriggerRef::can_wrap(obj2)?
        GrtObjectRef::cast_from(obj2)->owner()->owner()->name():
        GrtObjectRef::cast_from(obj2)->owner()->name();
    std::string create;
    if (db_TriggerRef::can_wrap(obj1))
        create = "CREATE ";
    else if (db_ViewRef::can_wrap(obj1))
        create.append("CREATE VIEW ").append(db_ViewRef::cast_from(obj1)->name()).append(" AS ");
    else if (db_RoutineRef::can_wrap(obj1))
        create.append("CREATE ").append(db_RoutineRef::cast_from(obj1)->routineType()).append(" ").append(db_RoutineRef::cast_from(obj1)->name()).append(" ");
    sql1= parser->normalizeSqlStatement(create + sql1, schema1);
    sql2= parser->normalizeSqlStatement(create + sql2, schema2);
    sql1= parser->removeInterTokenSpaces(sql1);
    sql2= parser->removeInterTokenSpaces(sql2);
//    sql1= base::toupper(sql1);
//    sql2= base::toupper(sql2);
    //TODO: Fix normalize, so that it would add it
    sql1.erase(std::remove(sql1.begin(), sql1.end(), '`'), sql1.end());
    sql2.erase(std::remove(sql2.begin(), sql2.end(), '`'), sql2.end());
//    if (sql1 != sql2)
//            std::cout<<"\n=======================Def1=======================\n"<<sql1<<std::endl<<"\n=======================Def2=======================\n"<<std::endl<<sql2<<"!!!"<<(sql1 == sql2)<<std::endl;
    return sql1 == sql2;
}

bool caseless_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name, const std::string& default_name)
{
    std::string str1= base::toupper(ObjectRef::cast_from(obj1).get_string_member(name));
    std::string str2= base::toupper(ObjectRef::cast_from(obj2).get_string_member(name));
    if (str1 == default_name)
        str1 = "";
    if (str2 == default_name)
        str2 = "";
    return str1 == str2;  
}

bool caseless_compare_arr(const ValueRef obj1, const ValueRef obj2, const std::string& name, const std::vector<std::string>& default_names)
{
    std::string str1= base::toupper(ObjectRef::cast_from(obj1).get_string_member(name));
    std::string str2= base::toupper(ObjectRef::cast_from(obj2).get_string_member(name));
    if (std::find(default_names.begin(),default_names.end(),str1) != default_names.end())
        str1 = "";
    if (std::find(default_names.begin(),default_names.end(),str2) != default_names.end())
        str2 = "";
    return str1 == str2;  
}

bool charset_collation_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name)
{
    std::string sql1= ObjectRef::cast_from(obj1).get_string_member(name);
    std::string sql2= ObjectRef::cast_from(obj2).get_string_member(name);

    if (db_ColumnRef::can_wrap(obj1))
    {
        std::string fieldname(name == "characterSetName"?"defaultCharacterSetName":"defaultCollationName");
        sql1= ObjectRef::cast_from(obj1).get_string_member(name);
        sql2= ObjectRef::cast_from(obj2).get_string_member(name);
        if (sql1.empty())
            sql1 = GrtNamedObjectRef::cast_from(obj2)->owner().get_string_member(fieldname);
        if (sql2.empty())
            sql2 = GrtNamedObjectRef::cast_from(obj2)->owner().get_string_member(fieldname);
        if ((sql1 == sql2) || sql1.empty() || sql2.empty())
            return true;
    }else
    {
        sql1= ObjectRef::cast_from(obj1).get_string_member(name);
        sql2= ObjectRef::cast_from(obj2).get_string_member(name);
        if (sql1.empty() || sql2.empty())
            return true;

    }
    return sql1 == sql2;
}


bool fk_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name, grt::GRT* grt)
{
    // Here we do not compare the ability for engines to support foreign keys but
    // check if at both engines does not. This can be used to optimize further FK handling
    // (no need to check all the FKs then).
    grt::StringRef ename = db_mysql_TableRef::cast_from(obj1)->tableEngine();
    db_mysql_StorageEngineRef engine = bec::TableHelper::get_engine_by_name(grt, ename);

    //          if (!engine.is_valid() || !engine->supportsForeignKeys())
    //            return true;

    ename = db_mysql_TableRef::cast_from(obj2)->tableEngine();
    db_mysql_StorageEngineRef engine2 = bec::TableHelper::get_engine_by_name(grt, ename);

    if ((engine.is_valid() && !engine->supportsForeignKeys()) && (engine2.is_valid() && !engine2->supportsForeignKeys()))
        return true;

    return false;
}

bool formatted_type_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name, grt::GRT* grt)
{
    std::string sql1= ObjectRef::cast_from(obj1).get_string_member(name);
    std::string sql2= ObjectRef::cast_from(obj2).get_string_member(name);
    SqlFacade* parser = SqlFacade::instance_for_rdbms_name(grt, "Mysql");

    if (!parser)
        return false;

    sql1= parser->removeInterTokenSpaces(sql1);
    sql2= parser->removeInterTokenSpaces(sql2);
    //        if (sql1 != sql2)
    //        std::cout<<"============"<<sql1<<std::endl<<std::endl<<sql2<<"==========================="<<std::endl;
    return sql1 == sql2;
}

bool sql_definition_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name, grt::GRT* grt)
{
    if (!db_DatabaseDdlObjectRef::can_wrap(obj1))
        return false;
    db_DatabaseDdlObjectRef dbobj1 = db_DatabaseDdlObjectRef::cast_from(obj1);
    db_DatabaseDdlObjectRef dbobj2 = db_DatabaseDdlObjectRef::cast_from(obj2);
    int alg1 = dbobj1.has_member("algorithm")?dbobj1.get_integer_member("algorithm"):0;
    int alg2 = dbobj2.has_member("algorithm")?dbobj2.get_integer_member("algorithm"):0;
    return sqlBody_compare(obj1, obj2, "sqlBody", grt) && (alg1 == alg2) && (caseless_compare (obj1 , obj2, "definer","ROOT`@`LOCALHOST"));
    /*
    std::string sql1= ObjectRef::cast_from(obj1).get_string_member(name);
    std::string sql2= ObjectRef::cast_from(obj2).get_string_member(name);
    SqlFacade* parser = SqlFacade::instance_for_rdbms_name(grt, "Mysql");

    if (!parser)
        return false;
    std::string schema1 = db_TriggerRef::can_wrap(obj1)?
        GrtObjectRef::cast_from(obj1)->owner()->owner()->name():
    GrtObjectRef::cast_from(obj1)->owner()->name();
    std::string schema2 = db_TriggerRef::can_wrap(obj2)?
        GrtObjectRef::cast_from(obj2)->owner()->owner()->name():
    GrtObjectRef::cast_from(obj2)->owner()->name();
    sql1= parser->normalizeSqlStatement(sql1, schema1);
    sql2= parser->normalizeSqlStatement(sql2, schema2);
    sql1= base::toupper(parser->removeInterTokenSpaces(sql1));
    sql2= base::toupper(parser->removeInterTokenSpaces(sql2));
    //        sql1.erase(std::remove(sql1.begin(), sql1.end(), '\n'), sql1.end());
    //        sql2.erase(std::remove(sql2.begin(), sql2.end(), '\n'), sql2.end());
    //        if (sql1 != sql2)
    //        std::cout<<"\n=======================Def1=======================\n"<<sql1<<std::endl<<"\n=======================Def2=======================\n"<<std::endl<<sql2<<"!!!"<<(sql1 == sql2)<<std::endl;
    return sql1 == sql2;
    */
}

bool ignore_index_col_name(const ValueRef obj1, const ValueRef obj2, const std::string& name)
{
    if(ObjectRef::cast_from(obj1).is_instance("db.IndexColumn") && 
        ObjectRef::cast_from(obj2).is_instance("db.IndexColumn") &&
        StringRef::can_wrap(ObjectRef::cast_from(obj1).get_member(name)) && 
        StringRef::can_wrap(ObjectRef::cast_from(obj2).get_member(name)))
        return true;
    return false;
}

std::string trim_zeros(const std::string& str)
{
  if (str.empty())
    return str;
  size_t pos = str.find_first_not_of("0");
  if (pos == std::string::npos)//there is only zeroes so return "0"
    return std::string("0");
  if (pos == 0)//no leading zeroes return unaltered string
    return str;
  return str.substr(pos);
}

std::string fixDefalutString(const std::string& str)
{
  if (str.empty())
    return str;
  if (str == std::string("0000-00-00 00:00:00")) return std::string("");
  if (strcasecmp(str.c_str(),"CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP") == 0) return std::string("CURRENT_TIMESTAMP");
  if (str == std::string("NOW()")) return std::string("CURRENT_TIMESTAMP");
  if (str == std::string("CURRENT_TIMESTAMP()")) return std::string("CURRENT_TIMESTAMP");
//  if (str == std::string("CURRENT_TIMESTAMP")) return std::string("CURRENT_TIMESTAMP");
  if (str == std::string("LOCALTIME()")) return std::string("CURRENT_TIMESTAMP");
  if (str == std::string("LOCALTIME")) return std::string("CURRENT_TIMESTAMP");
  if (str == std::string("LOCALTIMESTAMP")) return std::string("CURRENT_TIMESTAMP");
  if (str == std::string("LOCALTIMESTAMP()")) return std::string("CURRENT_TIMESTAMP");
  if (str == std::string("TRUE")) return std::string("1");
  if (str == std::string("FALSE")) return std::string("");
  if (strcasecmp(str.c_str(),"NULL") == 0) return std::string("");
  return trim_zeros(str);
};
//if(name1 == "defaultValue")
bool default_value_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name)
{
    std::string s1= ObjectRef::cast_from(obj1).get_string_member(name);
    std::string s2= ObjectRef::cast_from(obj2).get_string_member(name);
    s1.erase(std::remove_if(s1.begin(),s1.end(),std::bind2nd(std::equal_to<std::string::value_type>(),'\'')),s1.end());
    s2.erase(std::remove_if(s2.begin(),s2.end(),std::bind2nd(std::equal_to<std::string::value_type>(),'\'')),s2.end());
    s1 = fixDefalutString(s1);
    s2 = fixDefalutString(s2);
    return s1 == s2;
}  


bool name_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name)
{
    //db_ColumnRef name and old name should be always compared with case!
    if (db_ColumnRef::can_wrap(obj1))
        return false;
    std::string str1= base::toupper(ObjectRef::cast_from(obj1).get_string_member(name));
    std::string str2= base::toupper(ObjectRef::cast_from(obj2).get_string_member(name));
    return str1 == str2;  
}

bool ref_table_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name)
{
    std::string str1 = db_mysql_ForeignKeyRef::cast_from(obj1)->referencedTable().is_valid()?base::toupper(db_mysql_ForeignKeyRef::cast_from(obj1)->referencedTable()->name()):"";
    std::string str2 = db_mysql_ForeignKeyRef::cast_from(obj2)->referencedTable().is_valid()?base::toupper(db_mysql_ForeignKeyRef::cast_from(obj2)->referencedTable()->name()):"";
    return str1 == str2;
}

grt::NormalizedComparer::NormalizedComparer(grt::GRT* grt, const grt::DictRef options):_grt(grt)
{
  if (options.is_valid())
  {
    _case_sensitive = options.get_int("CaseSensitive") != 0;
    _maxTableCommentLength = options.get_int("maxTableCommentLength");
    _maxIndexCommentLength = options.get_int("maxIndexCommentLength");
    _maxColumnCommentLength = options.get_int("maxColumnCommentLength");
    load_rules();

  }else
  {
    _case_sensitive = true;
    _maxTableCommentLength = 60;
    _maxIndexCommentLength = 0;
    _maxColumnCommentLength = 255;
  }

  load_rules();
};

bool grt::NormalizedComparer::comment_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name)const
{
    std::string str1= ObjectRef::cast_from(obj1).get_string_member(name);
    std::string str2= ObjectRef::cast_from(obj2).get_string_member(name);
    int comment_len = 60;
    if (ObjectRef::cast_from(obj1).is_instance("db.IndexColumn"))
      comment_len = _maxIndexCommentLength;
    else if (ObjectRef::cast_from(obj1).is_instance("db.Table"))
      comment_len = _maxTableCommentLength;    
    else if (ObjectRef::cast_from(obj1).is_instance("db.Column"))
      comment_len = _maxColumnCommentLength;

    str1 = bec::TableHelper::get_sync_comment(str1, comment_len);
    str2 = bec::TableHelper::get_sync_comment(str2, comment_len);
    if (db_mysql_SchemaRef::can_wrap(obj1))
        return true;
    return str1 == str2;
}

bool supports_autoincement(const db_ColumnRef& column)
{
    db_SimpleDatatypeRef columnType;

    // Determine actually used column type first.
    if (column->userType().is_valid() && column->userType()->actualType().is_valid())
      columnType= column->userType()->actualType();
    else
      if (column->simpleType().is_valid() && column->simpleType()->group().is_valid())
        columnType= column->simpleType();
    

    return (columnType.is_valid() && columnType->group().is_valid()
      && !strcmp(columnType->group()->name().c_str(), "numeric"));

}
bool autoincrement_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name)
{
   if (db_ColumnRef::can_wrap(obj1))
   {
       db_ColumnRef col1 = db_ColumnRef::cast_from(obj1);
       db_ColumnRef col2 = db_ColumnRef::cast_from(obj2);
       return !supports_autoincement(col1) || !supports_autoincement(col2);
   }
   return false;
}

bool default_int_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name)
{
    int i1 = ObjectRef::cast_from(obj1).get_integer_member(name);
    int i2 = ObjectRef::cast_from(obj2).get_integer_member(name);
    return (i1 == -1) || (i2 == -1);
}

bool returnDatatype_compare(const ValueRef obj1, const ValueRef obj2, const std::string& name)
{
  if (!db_mysql_RoutineRef::can_wrap(obj1) || !db_mysql_RoutineRef::can_wrap(obj2))
      return false;
  db_mysql_RoutineRef r1 = db_mysql_RoutineRef::cast_from(obj1);
  db_mysql_RoutineRef r2 = db_mysql_RoutineRef::cast_from(obj2);
  grt::ListRef<db_UserDatatype> user_types1;
  grt::ListRef<db_SimpleDatatype> default_type_list1;
  
  if (r1->owner().is_valid() && r1->owner()->owner().is_valid())
  {
    db_CatalogRef catalog= db_CatalogRef::cast_from(r1->owner()->owner());
    user_types1 = catalog->userDatatypes();
    default_type_list1 = catalog->simpleDatatypes();
  }
  
  grt::ListRef<db_SimpleDatatype> types1;
  if(r1->owner()/*schema*/->owner()/*catalog*/->owner()/*phys.model*/.is_valid())
    types1 = db_mgmt_RdbmsRef::cast_from(r1->owner()/*schema*/->owner()/*catalog*/->owner()/*phys.model*/->get_member("rdbms"))->simpleDatatypes();
  db_UserDatatypeRef userType1;
  db_SimpleDatatypeRef simpleType1;
  int precision1 = bec::EMPTY_COLUMN_PRECISION;
  int scale1 = bec::EMPTY_COLUMN_SCALE;
  int length1 = bec::EMPTY_COLUMN_LENGTH;
  std::string datatypeExplicitParams1;
  std::string type1 = r1->returnDatatype();
  if (!bec::parseType(type1, GrtVersionRef(), types1,    user_types1,    default_type_list1,
      simpleType1, userType1, precision1,    scale1,    length1,    datatypeExplicitParams1))
      return false;

  grt::ListRef<db_UserDatatype> user_types2;
  grt::ListRef<db_SimpleDatatype> default_type_list2;
  
  if (r2->owner().is_valid() && r2->owner()->owner().is_valid())
  {
    db_CatalogRef catalog= db_CatalogRef::cast_from(r2->owner()->owner());
    user_types2 = catalog->userDatatypes();
    default_type_list2 = catalog->simpleDatatypes();
  }
  
  grt::ListRef<db_SimpleDatatype> types2;
  if(r2->owner()/*schema*/->owner()/*catalog*/->owner()/*phys.model*/.is_valid())
    types2 = db_mgmt_RdbmsRef::cast_from(r2->owner()/*schema*/->owner()/*catalog*/->owner()/*phys.model*/->get_member("rdbms"))->simpleDatatypes();
  db_UserDatatypeRef userType2;
  db_SimpleDatatypeRef simpleType2;
  int precision2 = bec::EMPTY_COLUMN_PRECISION;
  int scale2 = bec::EMPTY_COLUMN_SCALE;
  int length2 = bec::EMPTY_COLUMN_LENGTH;
  std::string datatypeExplicitParams2;
  std::string type2 = r1->returnDatatype();
  if (!bec::parseType(type2, GrtVersionRef(), types2,    user_types2,    default_type_list2,
      simpleType2, userType2, precision2,    scale2,    length2,    datatypeExplicitParams2))
      return false;

  return (simpleType1 == simpleType2) && (userType1 == userType2) && (precision1 == precision2) && 
      (scale1 == scale2) && (length1 = length2) && (datatypeExplicitParams1 == datatypeExplicitParams2);
}

void grt::NormalizedComparer::load_rules()
{
    static const std::vector<std::string> rules_defaults = boost::assign::list_of(std::string("RESTRICT"))("NO ACTION");
    rules.clear();
    rules["owner"].push_back(boost::bind(boost::function<bool ()> (boost::lambda::constant(true))));
    
    if(!_case_sensitive)
    {
    rules["name"].push_back(boost::bind(&name_compare, _1 ,_2, _3));
    rules["oldName"].push_back(boost::bind(&name_compare, _1 ,_2, _3));
    rules["referencedTable"].push_back(boost::bind(&ref_table_compare, _1 ,_2, _3));
    }

    //For IndexColumn names should be ignored
    rules["name"].push_back(boost::bind(&ignore_index_col_name, _1 ,_2, _3));

    rules["rowFormat"].push_back(boost::bind(&caseless_compare, _1 ,_2, _3,"DEFAULT"));
    rules["packKeys"].push_back(boost::bind(&caseless_compare, _1 ,_2, _3,"DEFAULT"));
    rules["tableEngine"].push_back(boost::bind(&caseless_compare, _1 ,_2, _3,""));
    rules["returnDatatype"].push_back(boost::bind(&caseless_compare, _1 ,_2, _3,""));
    rules["deleteRule"].push_back(boost::bind(&caseless_compare_arr, _1 ,_2, _3,rules_defaults));
    rules["updateRule"].push_back(boost::bind(&caseless_compare_arr, _1 ,_2, _3,rules_defaults));
    rules["comment"].push_back(boost::bind(&grt::NormalizedComparer::comment_compare,this , _1 ,_2, _3));
    rules["collationName"].push_back(boost::bind(&charset_collation_compare, _1 ,_2, _3));
    rules["defaultCollationName"].push_back(boost::bind(&charset_collation_compare, _1 ,_2, _3));
    rules["characterSetName"].push_back(boost::bind(&charset_collation_compare, _1 ,_2, _3));
    rules["defaultCharacterSetName"].push_back(boost::bind(&charset_collation_compare, _1 ,_2, _3));
    rules["foreignKeys"].push_back(boost::bind(&fk_compare, _1 ,_2, _3, _4));
    rules["formattedRawType"].push_back(boost::bind(&formatted_type_compare, _1 ,_2, _3, _4));
    rules["formattedType"].push_back(boost::bind(&formatted_type_compare, _1 ,_2, _3, _4));
    rules["sqlDefinition"].push_back(boost::bind(&sql_definition_compare, _1 ,_2, _3, _4));
    rules["precision"].push_back(boost::bind(&default_int_compare, _1 ,_2, _3));
    rules["length"].push_back(boost::bind(&default_int_compare, _1 ,_2, _3));
//    rules["sqlDefinition"].push_back(boost::bind(boost::function<bool ()> (boost::lambda::constant(true))));
    rules["definer"].push_back(boost::bind(&caseless_compare, _1 ,_2, _3,"ROOT`@`LOCALHOST"));
    rules["defaultValue"].push_back(boost::bind(&default_value_compare, _1 ,_2, _3));
    rules["autoIncrement"].push_back(boost::bind(&autoincrement_compare, _1 ,_2, _3));
    rules["sqlBody"].push_back(boost::bind(&sqlBody_compare, _1 ,_2, _3, _4));
    rules["returnDatatype"].push_back(boost::bind(&returnDatatype_compare, _1 ,_2, _3));
};

bool grt::NormalizedComparer::normalizedComparison(const ValueRef obj1, const ValueRef obj2, const std::string name)
{
    std::list<comparison_rule>& rul_list = rules[name];
    for(std::list<comparison_rule>::iterator It = rul_list.begin(); It != rul_list.end(); ++It)
        if ((*It)(obj1,obj2,name,_grt))
            return true;
    return false; 
};

void grt::NormalizedComparer::init_omf(Omf* omf)
{
    omf->case_sensitive = _case_sensitive;
    omf->normalizer = boost::bind(&NormalizedComparer::normalizedComparison,this, _1, _2, _3);
};

//TODO: This shouldn't be here but rather in DBPlugin, but QE doesn't use that
void grt::NormalizedComparer::load_db_options(sql::DatabaseMetaData *dbc_meta)
{
    _case_sensitive = dbc_meta->storesMixedCaseIdentifiers();
    const unsigned int major = dbc_meta->getDatabaseMajorVersion();
    const unsigned int minor = dbc_meta->getDatabaseMinorVersion();
    const unsigned int revision = dbc_meta->getDatabasePatchVersion();
    if ((major >= 5) && (minor >= 5) && (revision >= 5))
    {
        _maxTableCommentLength = 2048;
        _maxIndexCommentLength = 1024;
        _maxColumnCommentLength = 1024;
    }else
    {
        _maxTableCommentLength = 60;
        _maxIndexCommentLength = 0;
        _maxColumnCommentLength = 255;
    }
    load_rules();
};

grt::DictRef grt::NormalizedComparer::get_options_dict()const
{
    grt::DictRef result(_grt);
    result.set("CaseSensitive", grt::IntegerRef(_case_sensitive));
    result.set("maxTableCommentLength", grt::IntegerRef(_maxTableCommentLength));
    result.set("maxIndexCommentLength", grt::IntegerRef(_maxIndexCommentLength));
    result.set("maxColumnCommentLength", grt::IntegerRef(_maxColumnCommentLength));
    return result;
};