
#include "stdafx.h"

#include "db_mysql_diffsqlgen.h"

#include "diffchange.h"
#include "grtdiff.h"
#include "changeobjects.h"
#include "changelistobjects.h"

#include "db_mysql_diffsqlgen_grant.h"

#include "grt/common.h"

#include <ctype.h>

#include "module_db_mysql.h"

/* not used
static std::string get_table_old_name(db_mysql_TableRef table)
{
  return std::string("`").append(table->owner()->name().c_str()).append("`.`").append(table->oldName().c_str()).append("` ");
}
*/

void DiffSQLGeneratorBE::remember(const GrtNamedObjectRef &obj, const std::string &sql)
{
  if(target_list.is_valid())
  {
    target_list.insert(grt::StringRef(sql));
    if(target_object_list.is_valid())
      target_object_list.insert(obj);
  }
  else
  {
    target_map.set(get_full_object_name_for_key(obj), grt::StringRef(sql));
  }
}

void DiffSQLGeneratorBE::remember_alter(const GrtNamedObjectRef &obj, const std::string &sql)
{
  if(target_list.is_valid())
  {
    target_list.insert(grt::StringRef(sql));
    if(target_object_list.is_valid())
      target_object_list.insert(obj);
    return;
  }

  std::string key= get_full_object_name_for_key(obj);

  if(target_map.has_key(key))
  {
    grt::ValueRef value= target_map.get(key);
    if(grt::StringRef::can_wrap(value))
    {
      grt::StringListRef list_value(target_map.get_grt());
      list_value.insert(grt::StringRef::cast_from(value));
      list_value.insert(grt::StringRef(sql));
      target_map.set(key, list_value);
    }
    else if(grt::StringListRef::can_wrap(value))
    {
      grt::StringListRef::cast_from(value).insert(grt::StringRef(sql));
    }
    else
    {
      // a bug
      assert(0);
    }
  }
  else
  {
    target_map.set(key, grt::StringRef(sql));
  }
}


struct Part_sort_pred {
  bool operator() (const std::pair<int, grt::ValueRef>& v1, const std::pair<int, grt::ValueRef>& v2)
  {
    return v1 < v2;
  }
};

void DiffSQLGeneratorBE::generate_set_partitioning(db_mysql_TableRef table, 
                                                   const grt::DiffChange *table_diffchange)
{
  bool part_type_set= false, part_expr_set= false, 
    subpart_type_set= false, subpart_expr_set= false,
    part_count_set= false, part_defs_set= false;
  std::string part_type, part_expr, subpart_type, subpart_expr;
  grt::ListRef<db_mysql_PartitionDefinition> part_defs(table->get_grt());
  int part_count= 0;

  // gather all relevant attributes from change object
  const grt::ChangeSet *cs= table_diffchange->subchanges();
  for(grt::ChangeSet::const_iterator e= cs->end(), it= cs->begin(); it != e; it++)
  {
    const grt::ObjectAttrModifiedChange *attr_change= static_cast<const grt::ObjectAttrModifiedChange *>(*it);

    if(attr_change->get_attr_name().compare("partitionType") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      part_type.assign(grt::StringRef::cast_from(change->get_new_value()));
      part_type_set= true;
    }
    else if(attr_change->get_attr_name().compare("partitionExpression") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      part_expr.assign(grt::StringRef::cast_from(change->get_new_value()));
      part_expr_set= true;
    }
    else if(attr_change->get_attr_name().compare("subpartitionType") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      subpart_type.assign(grt::StringRef::cast_from(change->get_new_value()));
      subpart_type_set= true;
    }
    else if(attr_change->get_attr_name().compare("subpartitionExpression") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      subpart_expr.assign(grt::StringRef::cast_from(change->get_new_value()));
      subpart_expr_set= true;
    }
    else if(attr_change->get_attr_name().compare("partitionCount") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      part_count= grt::IntegerRef::cast_from(change->get_new_value());
      part_count_set= true;
    }
    else if(attr_change->get_attr_name().compare("partitionDefinitions") == 0)
    {
      const grt::ListChange *change= 
        static_cast<const grt::ListChange *>(attr_change->get_subchange());
      const grt::ChangeSet *part_defs_cs= change->subchanges();

      // needs to be sorted by LESS THAN value
      std::vector<std::pair<int, grt::ValueRef> > partition_vector;

      for(grt::ChangeSet::const_iterator part_defs_e= part_defs_cs->end(), 
        part_defs_it= part_defs_cs->begin(); part_defs_it != part_defs_e; part_defs_it++)
      {
        grt::ValueRef part_def_grt_value;

        const grt::DiffChange *part_def_change= *part_defs_it;
        if(part_def_change->get_change_type() == grt::ListItemAdded)
        {
          const grt::ListItemAddedChange *added_change= 
            static_cast<const grt::ListItemAddedChange *>(part_def_change);

          part_def_grt_value= added_change->get_action().first;
        }
        else if(part_def_change->get_change_type() == grt::ListItemModified)
        {
          const grt::ListItemModifiedChange *modified_change= 
            static_cast<const grt::ListItemModifiedChange  *>(part_def_change);

          part_def_grt_value= modified_change->get_action().second.first;
        }

        db_mysql_PartitionDefinitionRef part_def_value(db_mysql_PartitionDefinitionRef::cast_from(part_def_grt_value));

        partition_vector.push_back(std::pair<int, grt::ValueRef>(
          atoi(part_def_value->value().c_str()), part_def_value));
      }


      std::sort(partition_vector.begin(), partition_vector.end());

      for(std::vector<std::pair<int, grt::ValueRef> >::const_iterator pv_e= partition_vector.end(), 
        pv_it= partition_vector.begin(); pv_it != pv_e; pv_it++)
      {
        part_defs.insert(db_mysql_PartitionDefinitionRef::cast_from(pv_it->second));
      }

      part_defs_set= true;
    }
  }

  if(!part_type_set)
    part_type.assign(table->partitionType().c_str());
    
  if(!part_expr_set)
    part_expr.assign(table->partitionExpression().c_str());

  if(!subpart_type_set)
    subpart_type.assign(table->subpartitionType().c_str());

  if(!subpart_expr_set)
    subpart_expr.assign(table->subpartitionExpression().c_str());

  if(!part_count_set)
    part_count= table->partitionCount();

  if(!part_defs_set)
    part_defs= table->partitionDefinitions();

  callback->alter_table_generate_partitioning(table, part_type, part_expr, part_count, 
                        subpart_type, subpart_expr, part_defs);
}

void DiffSQLGeneratorBE::generate_create_partitioning(db_mysql_TableRef table)
{
  callback->alter_table_generate_partitioning(table, 
    std::string(table->partitionType().is_valid() ? table->partitionType().c_str() : ""),
    std::string(table->partitionExpression().is_valid() ? table->partitionExpression().c_str() : ""),
    table->partitionCount(),
    std::string(table->subpartitionType().is_valid() ? table->subpartitionType().c_str() : ""),
    std::string(table->subpartitionExpression().is_valid() ? table->subpartitionExpression().c_str() : ""),
    table->partitionDefinitions()
    );
}

void DiffSQLGeneratorBE::generate_create_stmt(db_mysql_TableRef table)
{
  bool process_table= true;
  std::string table_name_for_filter(get_full_object_name_for_key(table));
  if(_use_filtered_lists)
    if(_filtered_tables.find(table_name_for_filter) == _filtered_tables.end())
      process_table= false;

  if(process_table)
  {
    callback->create_table_props_begin(table);

    // note: for TIMESTAMP columns 'ON UPDATE' are stored in defaultValue field
    // see http://dev.mysql.com/doc/refman/5.1/en/timestamp.html for more info

    callback->create_table_columns_begin(table);

    grt::ListRef<db_mysql_Column> columns= table->columns();
    for(size_t columns_count= columns.count(), i= 0; i < columns_count; i++)
    {
      db_mysql_ColumnRef column= columns.get(i);
      callback->create_table_column(column);
    }

    callback->create_table_columns_end(table);

    // indices processing

    callback->create_table_indexes_begin(table);

    grt::ListRef<db_mysql_Index> indices= table->indices();
    for(size_t index_count= indices.count(), i= 0; i < index_count; i++)
    {
      db_mysql_IndexRef index= indices.get(i);

      bool separate_index= _gen_create_index && (index->isPrimary() == 0);
      callback->create_table_index(index, separate_index);
    }

    callback->create_table_indexes_end(table);

    callback->create_table_fks_begin(table);
    if (!_skip_foreign_keys) 
    { 

      grt::ListRef<db_mysql_ForeignKey> fks= table->foreignKeys();
      for(size_t fk_count= fks.count(), i= 0; i < fk_count; i++)
      {
        db_mysql_ForeignKeyRef fk= fks.get(i);
        if (fk.is_valid() && fk->referencedTable().is_valid())
        {
          if (fk->modelOnly() || fk->referencedTable()->modelOnly())
            continue;

          callback->create_table_fk(fk);
        }
      }
    }
    callback->create_table_fks_end(table);

    // TODO: [TABLESPACE tablespace_name STORAGE DISK]

    if(strlen(table->tableEngine().c_str()))
      callback->create_table_engine(table->tableEngine());

    if(strlen(table->nextAutoInc().c_str()))
      callback->create_table_next_auto_inc(table->nextAutoInc());

    if(strlen(table->avgRowLength().c_str()))
      callback->create_table_avg_row_length(table->avgRowLength());

    if(strlen(table->defaultCharacterSetName().c_str()))
      callback->create_table_charset(table->defaultCharacterSetName());

    if(table->checksum() != 0)
      callback->create_table_checksum(table->checksum());

    if(strlen(table->defaultCollationName().c_str()))
      callback->create_table_collate(table->defaultCollationName());

    if(strlen(table->comment().c_str()))
      callback->create_table_comment(table->comment());
    
    // CONNECTION [=] 'connect_string'

    if(strlen(table->tableDataDir().c_str()))
      callback->create_table_data_dir(table->tableDataDir());

    if(table->delayKeyWrite() != 0)
      callback->create_table_delay_key_write(table->delayKeyWrite());

    if(strlen(table->tableIndexDir().c_str()))
      callback->create_table_index_dir(table->tableIndexDir());

    if(strlen(table->mergeInsert().c_str()))
      callback->create_table_merge_insert(table->mergeInsert());

    // KEY_BLOCK_SIZE [=] value

    if(strlen(table->maxRows().c_str()))
      callback->create_table_max_rows(table->maxRows());

    if(strlen(table->minRows().c_str()))
      callback->create_table_min_rows(table->minRows());

    if(strlen(table->packKeys().c_str()))
      callback->create_table_pack_keys(table->packKeys());

    if(strlen(table->password().c_str()))
      callback->create_table_password(table->password());

    if(strlen(table->rowFormat().c_str()))
      callback->create_table_row_format(table->rowFormat());

    if(strlen(table->mergeUnion().c_str()))
      callback->create_table_merge_union(table->mergeUnion());

    if(table->partitionType().is_valid() && strlen(table->partitionType().c_str())
    && table->partitionExpression().is_valid() && strlen(table->partitionExpression().c_str()))
    {
      generate_create_partitioning(table);
    }

    callback->create_table_props_end(table);
  } // process_table

  grt::ListRef<db_mysql_Trigger> triggers= table->triggers();
  for(size_t c= triggers.count(), i= 0; i < c; i++)
  {
    db_mysql_TriggerRef trigger= triggers.get(i);
    generate_create_stmt(trigger);
  }
}

void DiffSQLGeneratorBE::generate_create_stmt(db_mysql_ViewRef view)
{
  std::string view_name_for_filter(get_full_object_name_for_key(view));
  if(_use_filtered_lists)
    if(_filtered_views.find(view_name_for_filter) == _filtered_views.end())
      return;

  callback->create_view(view);
}

void DiffSQLGeneratorBE::generate_create_stmt(db_mysql_RoutineRef routine, bool for_alter)
{
  std::string routine_name_for_filter(get_full_object_name_for_key(routine));
  if(_use_filtered_lists)
    if(_filtered_routines.find(routine_name_for_filter) == _filtered_routines.end())
      return;

  callback->create_routine(routine, for_alter);
}

void DiffSQLGeneratorBE::generate_create_stmt(db_mysql_TriggerRef trigger, bool for_alter)
{
  std::string trigger_name_for_filter(get_full_object_name_for_key(trigger));

  if(_use_filtered_lists)
    if(_filtered_triggers.find(trigger_name_for_filter) == _filtered_triggers.end())
      return;

  callback->create_trigger(trigger, for_alter);
}

void DiffSQLGeneratorBE::generate_create_stmt(db_mysql_SchemaRef schema)
{
  std::string schema_name_for_filter(get_full_object_name_for_key(schema));

  if(_use_filtered_lists)
    if(_filtered_schemata.find(schema_name_for_filter) == _filtered_schemata.end())
      return;

  callback->create_schema(schema);

  grt::ListRef<db_mysql_Table> tables= schema->tables();
  for(size_t count= tables.count(), i= 0; i < count; i++)
  {
    db_mysql_TableRef table= tables.get(i);
    generate_create_stmt(table);
  }

  grt::ListRef<db_mysql_View> views= schema->views();
  for(size_t count= views.count(), i= 0; i < count; i++)
  {
    db_mysql_ViewRef view= views.get(i);
    generate_create_stmt(view);
  }

  grt::ListRef<db_mysql_Routine> routines= schema->routines();
  for(size_t count= routines.count(), i= 0; i < count; i++)
  {
    db_mysql_RoutineRef routine= routines.get(i);
    generate_create_stmt(routine);
  }
}

void DiffSQLGeneratorBE::generate_create_stmt(db_UserRef user)
{
  std::string user_name_for_filter(get_full_object_name_for_key(user));

  if(_use_filtered_lists)
    if(_filtered_users.find(user_name_for_filter) == _filtered_users.end())
      return;

  callback->create_user(user);
}

void DiffSQLGeneratorBE::generate_create_stmt(db_mysql_CatalogRef catalog)
{
  grt::ListRef<db_mysql_Schema> schemata= catalog->schemata();
  for(size_t count= schemata.count(), i= 0; i < count; i++)
  {
    db_mysql_SchemaRef schema= schemata.get(i);
    generate_create_stmt(schema);
  }

  for(size_t count= catalog->users().count(), i= 0; i < count; i++)
  {
    db_UserRef user= catalog->users().get(i);
    generate_create_stmt(user);
  }
}

void DiffSQLGeneratorBE::generate_drop_stmt(db_mysql_TableRef table)
{
  std::string table_name_for_filter(get_full_object_name_for_key(table));
  if(_use_filtered_lists)
    if(_filtered_tables.find(table_name_for_filter) == _filtered_tables.end())
      return;

  callback->drop_table(table);

  grt::ListRef<db_mysql_Trigger> triggers= table->triggers();
  for(size_t c= triggers.count(), i= 0; i < c; i++)
  {
    db_mysql_TriggerRef trigger= triggers.get(i);
    generate_drop_stmt(trigger);
  }
}

void DiffSQLGeneratorBE::generate_drop_stmt(db_mysql_TriggerRef trigger, bool for_alter)
{
  std::string trigger_name_for_filter(get_full_object_name_for_key(trigger));
  if(_use_filtered_lists)
    if(_filtered_triggers.find(trigger_name_for_filter) == _filtered_triggers.end())
      return;

  callback->drop_trigger(trigger, for_alter);
}

void DiffSQLGeneratorBE::generate_drop_stmt(db_mysql_ViewRef view)
{
  std::string view_name_for_filter(get_full_object_name_for_key(view));
  if(_use_filtered_lists)
    if(_filtered_views.find(view_name_for_filter) == _filtered_views.end())
      return;

  callback->drop_view(view);
}

void DiffSQLGeneratorBE::generate_drop_stmt(db_mysql_RoutineRef routine, bool for_alter)
{
  std::string routine_name_for_filter(get_full_object_name_for_key(routine));
  if(_use_filtered_lists)
    if(_filtered_routines.find(routine_name_for_filter) == _filtered_routines.end())
      return;

  callback->drop_routine(routine, for_alter);
}

void DiffSQLGeneratorBE::generate_drop_stmt(db_UserRef user)
{
  callback->drop_user(user);
}

void DiffSQLGeneratorBE::generate_drop_stmt(db_mysql_SchemaRef schema)
{
  callback->drop_schema(schema);

  grt::ListRef<db_mysql_Table> tables= schema->tables();
  for(size_t count= tables.count(), i= 0; i < count; i++)
  {
    db_mysql_TableRef table= tables.get(i);
    generate_drop_stmt(table);
  }

  grt::ListRef<db_mysql_View> views= schema->views();
  for(size_t count= views.count(), i= 0; i < count; i++)
  {
    db_mysql_ViewRef view= views.get(i);
    generate_drop_stmt(view);
  }

  grt::ListRef<db_mysql_Routine> routines= schema->routines();
  for(size_t count= routines.count(), i= 0; i < count; i++)
  {
    db_mysql_RoutineRef routine= routines.get(i);
    generate_drop_stmt(routine);
  }
}

void DiffSQLGeneratorBE::generate_drop_stmt(db_mysql_CatalogRef catalog)
{
  grt::ListRef<db_mysql_Schema> schemata= catalog->schemata();
  for(size_t count= schemata.count(), i= 0; i < count; i++)
  {
    db_mysql_SchemaRef schema= schemata.get(i);
    generate_drop_stmt(schema);
  }

  for(size_t count= catalog->users().count(), i= 0; i < count; i++)
  {
    db_UserRef user= catalog->users().get(i);
    generate_drop_stmt(user);
  }
}

void DiffSQLGeneratorBE::generate_alter(grt::ListRef<db_mysql_Column> columns, const grt::ListChange *diffchange)
{
  grt::GrtListDifference work_diff= diffchange->get_actions();
  const grt::ChangeSet *columns_cs= diffchange->subchanges();

  if(columns.count() == 0)
    return;

  db_mysql_TableRef table= db_mysql_TableRef::cast_from(columns.get(0)->owner());

  callback->alter_table_columns_begin(table);

  Column_rename_map column_rename_map;

  // build a map of column renames
  for(grt::ChangeSet::const_iterator e= columns_cs->end(), it= columns_cs->begin(); it != e; it++)
  {
    // process CHANGE COLUMN (handles content change only)
    for(grt::ChangeSet::const_iterator e= columns_cs->end(), it= columns_cs->begin(); it != e; it++)
    {
      const grt::DiffChange *column_change= *it;

      if(column_change->get_change_type() == grt::ListItemModified)
      {
        const grt::ListItemModifiedChange *modified_change= static_cast<const grt::ListItemModifiedChange *>(column_change);
        db_mysql_ColumnRef column= db_mysql_ColumnRef::cast_from(grt::ValueRef(modified_change->get_action().second.first));
        if(strcmp(column->name().c_str(), column->oldName().c_str()))
          column_rename_map[std::string(column->oldName().c_str())]= std::string(column->name().c_str());
      }
      else if(column_change->get_change_type() == grt::ListItemOrderChanged)
      {
        const grt::ListItemOrderChange *order_change= static_cast<const grt::ListItemOrderChange *>(column_change);
        db_mysql_ColumnRef org_col= db_mysql_ColumnRef::cast_from(grt::ValueRef(order_change->get_action().first.first));
        db_mysql_ColumnRef mod_col= db_mysql_ColumnRef::cast_from(grt::ValueRef(order_change->get_action().second.first));
        if(strcmp(org_col->name().c_str(), mod_col->oldName().c_str()))
          column_rename_map[std::string(org_col->oldName().c_str())]= std::string(mod_col->name().c_str());
      }
    }
  }

  // process DROP COLUMN
  for(grt::ChangeSet::const_iterator e= columns_cs->end(), it= columns_cs->begin(); it != e; it++)
  {
    const grt::DiffChange *column_change= *it;
    if(column_change->get_change_type() != grt::ListItemRemoved)
      continue;

    const grt::ListItemRemovedChange *removed_change= static_cast<const grt::ListItemRemovedChange *>(column_change);
    grt::GrtListDifference::Item action = removed_change->get_action();
    work_diff.apply_removed(action);
    callback->alter_table_drop_column(table, db_mysql_ColumnRef::cast_from(removed_change->get_action().first));
  }

  // process ADD COLUMN
  for(grt::ChangeSet::const_iterator e= columns_cs->end(), it= columns_cs->begin(); it != e; it++)
  {
    const grt::DiffChange *column_change= *it;
    if(column_change->get_change_type() != grt::ListItemAdded)
      continue;

    const grt::ListItemAddedChange *added_change= static_cast<const grt::ListItemAddedChange *>(column_change);

    grt::GrtListDifference::Item action =added_change->get_action();
    int index= work_diff.apply_added(action);
    grt::ValueRef *v= NULL;
    if(index > 0)
      v= work_diff.get_id(index-1);

    callback->alter_table_add_column(
      table, 
      column_rename_map,
      db_mysql_ColumnRef::cast_from(grt::ValueRef(added_change->get_action().first)),
      v == NULL ? db_mysql_ColumnRef() : db_mysql_ColumnRef::cast_from(grt::ValueRef(*v)));
  }

  std::set<grt::ValueRef> processed_items;

  // process CHANGE COLUMN (handles both position and content change)
  for(grt::ChangeSet::const_iterator e= columns_cs->end(), it= columns_cs->begin(); it != e; it++)
  {
    const grt::DiffChange *column_change= *it;

    if(column_change->get_change_type() != grt::ListItemOrderChanged)
      continue;

    const grt::ListItemOrderChange *order_change= 
      static_cast<const grt::ListItemOrderChange *>(column_change);
    // index_pair.second is the new position
    grt::GrtListDifference::ItemPair action = order_change->get_action();
    std::pair<int, int> index_pair= work_diff.apply_moved(action);
    grt::ValueRef *v= NULL;
    if(index_pair.second > 0)
      v= work_diff.get_id(index_pair.second-1);

    callback->alter_table_change_column(
      table,
      db_mysql_ColumnRef::cast_from(grt::ValueRef(order_change->get_action().first.first)),
      db_mysql_ColumnRef::cast_from(grt::ValueRef(order_change->get_action().second.first)),
      v == NULL ? db_mysql_ColumnRef() : db_mysql_ColumnRef::cast_from(grt::ValueRef(*v)),
      false,
      column_rename_map
    );
  }

  // process CHANGE COLUMN (handles content change only)
  for(grt::ChangeSet::const_iterator e= columns_cs->end(), it= columns_cs->begin(); it != e; it++)
  {
    const grt::DiffChange *column_change= *it;

    if(column_change->get_change_type() != grt::ListItemModified)
      continue;
  
    const grt::ListItemModifiedChange *modified_change= static_cast<const grt::ListItemModifiedChange *>(column_change);

    callback->alter_table_change_column(
      table,
      db_mysql_ColumnRef::cast_from(grt::ValueRef(modified_change->get_action().second.first)),
      db_mysql_ColumnRef(),
      db_mysql_ColumnRef(),
      true,
      column_rename_map
    );
  }

  callback->alter_table_columns_end(table);
}

void DiffSQLGeneratorBE::generate_alter(grt::ListRef<db_mysql_Index> indices, const grt::ListChange *diffchange)
{
  grt::GrtListDifference work_diff= diffchange->get_actions();
  const grt::ChangeSet *indices_cs= diffchange->subchanges();

  for(grt::ChangeSet::const_iterator e= indices_cs->end(), it= indices_cs->begin(); it != e; it++)
  {
    const grt::DiffChange *index_change= *it;
    
    switch(index_change->get_change_type())
    {
    case grt::ListItemAdded:  // ADD INDEX
      {
        const grt::ListItemAddedChange *added_change= static_cast<const grt::ListItemAddedChange *>(index_change);
        grt::GrtListDifference::Item action = added_change->get_action();
        work_diff.apply_added(action);
        callback->alter_table_add_index(db_mysql_IndexRef::cast_from(added_change->get_action().first));
      }
      break;
    case grt::ListItemRemoved:  // DROP INDEX
      {
        const grt::ListItemRemovedChange *removed_change= static_cast<const grt::ListItemRemovedChange *>(index_change);
        grt::GrtListDifference::Item action = removed_change->get_action(); 
        work_diff.apply_removed(action);
        callback->alter_table_drop_index(db_mysql_IndexRef::cast_from(removed_change->get_action().first));
      }
      break;
    case grt::ListItemOrderChanged: // DROP/ADD INDEX
      {
        const grt::ListItemOrderChange *order_change= static_cast<const grt::ListItemOrderChange *>(index_change);
        // index_pair.second is the new position
        grt::GrtListDifference::ItemPair action = order_change->get_action();
        work_diff.apply_moved(action);

        callback->alter_table_drop_index(db_mysql_IndexRef::cast_from(order_change->get_action().first.first));
        callback->alter_table_add_index(db_mysql_IndexRef::cast_from(order_change->get_action().second.first));
      }
      break;

    case grt::ListItemModified: 
      {
        const grt::ListItemModifiedChange *modified_change= static_cast<const grt::ListItemModifiedChange *>(index_change);

        callback->alter_table_drop_index(db_mysql_IndexRef::cast_from(modified_change->get_action().second.first));
        callback->alter_table_add_index(db_mysql_IndexRef::cast_from(modified_change->get_action().second.first));
      }
      break;
      default: break;
    }
  }
}

void DiffSQLGeneratorBE::generate_alter(
  grt::ListRef<db_mysql_ForeignKey> fks, const grt::ListChange *diffchange)
{
/*
  You cannot add a foreign key and drop a foreign key in separate 
  clauses of a single ALTER TABLE  statement. You must use separate 
  statements.
*/

  grt::GrtListDifference work_diff= diffchange->get_actions();
  const grt::ChangeSet *fks_cs= diffchange->subchanges();

  for(grt::ChangeSet::const_iterator e= fks_cs->end(), it= fks_cs->begin(); it != e; it++)
  {
    const grt::DiffChange *fk_change= *it;
    
    db_mysql_ForeignKeyRef fk1, fk2;
    switch(fk_change->get_change_type())
    {
    case grt::ListItemAdded:  // ADD FK
      {
        const grt::ListItemAddedChange *added_change= static_cast<const grt::ListItemAddedChange *>(fk_change);
        fk1= db_mysql_ForeignKeyRef::cast_from(added_change->get_action().first);
      }
      break;
    case grt::ListItemRemoved:  // DROP FK
      {
        const grt::ListItemRemovedChange *removed_change= static_cast<const grt::ListItemRemovedChange *>(fk_change);
        fk1= db_mysql_ForeignKeyRef::cast_from(removed_change->get_action().first);
      }
      break;
    case grt::ListItemOrderChanged: // DROP/ADD FK
      {
        const grt::ListItemOrderChange *order_change= static_cast<const grt::ListItemOrderChange *>(fk_change);
        fk1= db_mysql_ForeignKeyRef::cast_from(order_change->get_action().first.first);
        fk2= db_mysql_ForeignKeyRef::cast_from(order_change->get_action().second.first);
      }
      break;
    case grt::ListItemModified: 
      {
        const grt::ListItemModifiedChange *modified_change= static_cast<const grt::ListItemModifiedChange *>(fk_change);
        fk1= db_mysql_ForeignKeyRef::cast_from(modified_change->get_action().first.first);
        fk2= db_mysql_ForeignKeyRef::cast_from(modified_change->get_action().second.first);
      }
      break;
    default:
      break;
    }
    if (fk1.is_valid() && (fk1->modelOnly() || !fk1->referencedTable().is_valid() || fk1->referencedTable()->modelOnly()))
      continue;
    if (fk2.is_valid() && (fk2->modelOnly() || !fk2->referencedTable().is_valid() || fk2->referencedTable()->modelOnly()))
      continue;

    switch(fk_change->get_change_type())
    {
    case grt::ListItemAdded:  // ADD FK
      {
        const grt::ListItemAddedChange *added_change= static_cast<const grt::ListItemAddedChange *>(fk_change);
        grt::GrtListDifference::Item action = added_change->get_action();
        work_diff.apply_added(action);
        callback->alter_table_add_fk(db_mysql_ForeignKeyRef::cast_from(added_change->get_action().first));
      }
      break;
    case grt::ListItemRemoved:  // DROP FK
      {
        const grt::ListItemRemovedChange *removed_change= static_cast<const grt::ListItemRemovedChange *>(fk_change);
        grt::GrtListDifference::Item action = removed_change->get_action();
        work_diff.apply_removed(action);
        callback->alter_table_drop_fk(db_mysql_ForeignKeyRef::cast_from(removed_change->get_action().first));
      }
      break;
    case grt::ListItemOrderChanged: // DROP/ADD FK
      {
        const grt::ListItemOrderChange *order_change= static_cast<const grt::ListItemOrderChange *>(fk_change);
        // index_pair.second is the new position
        grt::GrtListDifference::ItemPair action = order_change->get_action(); 
        work_diff.apply_moved(action);
        callback->alter_table_drop_fk(db_mysql_ForeignKeyRef::cast_from(order_change->get_action().first.first));
        callback->alter_table_add_fk(db_mysql_ForeignKeyRef::cast_from(order_change->get_action().second.first));
      }
      break;

    case grt::ListItemModified: 
      {
        const grt::ListItemModifiedChange *modified_change= static_cast<const grt::ListItemModifiedChange *>(fk_change);
        callback->alter_table_drop_fk(db_mysql_ForeignKeyRef::cast_from(modified_change->get_action().first.first));
        callback->alter_table_add_fk(db_mysql_ForeignKeyRef::cast_from(modified_change->get_action().second.first));
      }
      break;
      default: break;
    }
  }
}

void DiffSQLGeneratorBE::generate_alter_stmt(db_mysql_TableRef table, const grt::DiffChange *diffchange)
{
  bool process_table= true;

  std::string table_name_for_filter(get_full_object_name_for_key(table));
  if(_use_filtered_lists)
    if(_filtered_tables.find(table_name_for_filter) == _filtered_tables.end())
      process_table= false;

  // process table triggers
  const grt::ChangeSet *cs= diffchange->subchanges();

  for(grt::ChangeSet::const_iterator e= cs->end(), it= cs->begin(); it != e; it++)
  {
    const grt::ObjectAttrModifiedChange *attr_change= 
      static_cast<const grt::ObjectAttrModifiedChange *>(*it);

    if(attr_change->get_attr_name().compare("triggers") == 0)
    {
      const grt::ListChange *list_change= 
        static_cast<const grt::ListChange *>(attr_change->get_subchange());
      
      const grt::ChangeSet *triggers_cs= list_change->subchanges();
      
      for(grt::ChangeSet::const_iterator e2= triggers_cs->end(), jt= triggers_cs->begin(); jt != e2; jt++)
      {
        const grt::DiffChange *trigger_change= *jt;

        switch(trigger_change->get_change_type())
        {
        case grt::ListItemAdded:
          {
            db_mysql_TriggerRef trigger(db_mysql_TriggerRef::cast_from(
              static_cast<const grt::ListItemAddedChange *>(trigger_change)->get_added_object()));
            generate_create_stmt(trigger, false);
          }
          break;
        case grt::ListItemRemoved:
          {
            db_mysql_TriggerRef trigger(db_mysql_TriggerRef::cast_from(table->triggers().get(
                static_cast<const grt::ListItemRemovedChange *>(trigger_change)->get_source_object_index())));
            generate_drop_stmt(trigger, false);
          }
          break;
        case grt::ListItemModified:
          {
            db_mysql_TriggerRef old_trigger= db_mysql_TriggerRef::cast_from(
              grt::ValueRef(static_cast<const grt::ListItemModifiedChange *>
              (trigger_change)->get_action().first.first));
            db_mysql_TriggerRef new_trigger= db_mysql_TriggerRef::cast_from(
              grt::ValueRef(static_cast<const grt::ListItemModifiedChange *>
              (trigger_change)->get_action().second.first));

            generate_drop_stmt(old_trigger);
            generate_create_stmt(new_trigger);
          }
          break;
        // list item position change is not relevant        
          default: break;
        }
      }
    }
  }

  if(!process_table)
    return;

  bool partitions_processed= false;
  cs= diffchange->subchanges();

  callback->alter_table_props_begin(table);

  for(grt::ChangeSet::const_iterator e= cs->end(), it= cs->begin(); it != e; it++)
  {
    const grt::ObjectAttrModifiedChange *attr_change= 
      static_cast<const grt::ObjectAttrModifiedChange *>(*it);

    if(attr_change->get_attr_name().compare("name") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_name(table, str);
    }
    if(attr_change->get_attr_name().compare("columns") == 0)
    {
      const grt::ListChange *list_change= 
        static_cast<const grt::ListChange *>(attr_change->get_subchange());
      generate_alter(table->columns(), list_change);
    }
    else if(attr_change->get_attr_name().compare("indices") == 0)
    {
      const grt::ListChange *list_change= 
        static_cast<const grt::ListChange *>(attr_change->get_subchange());
      callback->alter_table_indexes_begin(table);
      generate_alter(table->indices(), list_change);
      callback->alter_table_indexes_end(table);
    }
    else if(attr_change->get_attr_name().compare("foreignKeys") == 0)
    {
      const grt::ListChange *list_change= 
        static_cast<const grt::ListChange *>(attr_change->get_subchange());
      
      callback->alter_table_fks_begin(table);
      generate_alter(table->foreignKeys(), list_change);
      callback->alter_table_fks_end(table);
    }
    else if(attr_change->get_attr_name().compare("tableEngine") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_engine(table, str);
    }
    else if(attr_change->get_attr_name().compare("nextAutoInc") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_next_auto_inc(table, str);
    }
    else if(attr_change->get_attr_name().compare("password") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_password(table, str);
    }
    else if(attr_change->get_attr_name().compare("delayKeyWrite") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::IntegerRef n= grt::IntegerRef::cast_from(change->get_new_value());
      callback->alter_table_delay_key_write(table, n);
    }
    else if(attr_change->get_attr_name().compare("defaultCharacterSetName") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_charset(table, str);
    }
    else if(attr_change->get_attr_name().compare("defaultCollationName") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_collate(table, str);
    }
    else if(attr_change->get_attr_name().compare("mergeUnion") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_merge_union(table, str);
    }
    else if(attr_change->get_attr_name().compare("mergeInsert") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_merge_insert(table, str);
    }
    // ALTER TABLE silently ignores these attributes
#if 0
    else if(attr_change->get_attr_name().compare("tableDataDir") == 0)
    {
      const grt::SimpleValueChange *change= static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      alter_sql.append("DATA DIRECTORY = '").append(str.c_str()).append("' ");
    }
    else if(attr_change->get_attr_name().compare("tableIndexDir") == 0)
    {
      const grt::SimpleValueChange *change= static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      alter_sql.append("INDEX DIRECTORY = '").append(str.c_str()).append("' ");
    }
#endif
    else if(attr_change->get_attr_name().compare("packKeys") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_pack_keys(table, str);
    }
    else if(attr_change->get_attr_name().compare("checksum") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::IntegerRef n= grt::IntegerRef::cast_from(change->get_new_value());
      callback->alter_table_checksum(table, n);
    }
    else if(attr_change->get_attr_name().compare("comment") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_comment(table, str);
    }
    else if(attr_change->get_attr_name().compare("rowFormat") == 0)
    {
      const grt::SimpleValueChange *change= static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_row_format(table, str);
    }
    else if(attr_change->get_attr_name().compare("avgRowLength") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_avg_row_length(table, str);
    }
    else if(attr_change->get_attr_name().compare("minRows") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_min_rows(table, str);
    }
    else if(attr_change->get_attr_name().compare("maxRows") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_max_rows(table, str);
    }
    // to be added later, probably needs support for CREATE/ALTER SERVER
#if 0
    else if(attr_change->get_attr_name().compare("connection") == 0)
    {
    }
#endif
    else if(attr_change->get_attr_name().compare("connectionString") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
      callback->alter_table_connection_string(table, str);
    }
    else if((attr_change->get_attr_name().compare("partitionType") == 0) 
       || (attr_change->get_attr_name().compare("partitionExpression") == 0)
       || (attr_change->get_attr_name().compare("subpartitionType") == 0)
       || (attr_change->get_attr_name().compare("subpartitionExpression") == 0)
       || (attr_change->get_attr_name().compare("subpartitionCount") == 0))
    {
      if(partitions_processed)
        continue;

      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::StringRef new_str= grt::StringRef::cast_from(change->get_new_value());
      grt::StringRef old_str= grt::StringRef::cast_from(change->get_old_value());

      if(strlen(new_str.c_str()) == 0) // drop partitioning
        callback->alter_table_drop_partitioning(table);
      else  // add/change partitioning
        generate_set_partitioning(table, diffchange);

      partitions_processed= true;
    }
    else
    {
      continue; // avoid setting comma, see below
    }
  }

  bool is_range= (strcmp(table->partitionType().c_str(), "RANGE") == 0);

  // partitioning options that dont require PARTITION BY clause
  for(grt::ChangeSet::const_iterator e= cs->end(), it= cs->begin(); 
    (it != e) && !partitions_processed; it++)
  {
    const grt::ObjectAttrModifiedChange *attr_change= 
      static_cast<const grt::ObjectAttrModifiedChange *>(*it);

    if(attr_change->get_attr_name().compare("partitionCount") == 0)
    {
      const grt::SimpleValueChange *change= 
        static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
      grt::IntegerRef new_count= grt::IntegerRef::cast_from(change->get_new_value());
      
      //std::string part_count_sql(generate_change_partition_count(table, new_count));

      //// partition count alone can be changed only for HASH/KEY partitions
      //// generate_change_partition_count() will return empty string otherwise
      //// for RANGE/LIST we ignore change of this attribute and rely solely on
      //// partition definitions change
      //if(!part_count_sql.empty())
      //{
      //  alter_sql.append(comma).append(part_count_sql);
      //  partitions_processed= true;
      //}

      std::string part_type(table->partitionType().c_str());

      if((part_type.find("HASH") != std::string::npos)
          || (part_type.find("KEY") != std::string::npos))
      {
        callback->alter_table_partition_count(table, new_count);
        partitions_processed= true;
      }
    }
    else if(attr_change->get_attr_name().compare("partitionDefinitions") == 0)
    {
      const grt::ListChange *list_change= static_cast<const grt::ListChange *>(attr_change->get_subchange());
      const grt::ChangeSet *part_cs= list_change->subchanges();
      for(grt::ChangeSet::const_iterator e2= part_cs->end(), jt= part_cs->begin(); jt != e2; jt++)
      {
        const grt::DiffChange *part_change= *jt;
        switch(part_change->get_change_type())
        {
        case grt::ListItemAdded:
          {
            db_mysql_PartitionDefinitionRef part(db_mysql_PartitionDefinitionRef::cast_from(
              static_cast<const grt::ListItemAddedChange *>(part_change)->get_added_object()));
            callback->alter_table_add_partition(part, is_range);
          }
          break;
        case grt::ListItemRemoved:
          {
            db_mysql_PartitionDefinitionRef part= table->partitionDefinitions().get(
              static_cast<const grt::ListItemRemovedChange *>(part_change)->get_source_object_index());

            callback->alter_table_drop_partition(part->name().c_str());
          }
          break;
        case grt::ListItemModified:
          {
            db_mysql_PartitionDefinitionRef old_part(db_mysql_PartitionDefinitionRef::cast_from(
              static_cast<const grt::ListItemModifiedChange *>(part_change)->get_action().first.first));
            db_mysql_PartitionDefinitionRef new_part(db_mysql_PartitionDefinitionRef::cast_from(
              static_cast<const grt::ListItemModifiedChange *>(part_change)->get_action().second.first));

            callback->alter_table_reorganize_partition(old_part, new_part, is_range);
          }
          break;
          default: break;
        }

        partitions_processed= true;
      }
    }
  }

  callback->alter_table_props_end(table);
}

void DiffSQLGeneratorBE::generate_alter_stmt(db_mysql_ViewRef old_view, db_mysql_ViewRef new_view, const grt::DiffChange *diffchange)
{
  std::string view_name_for_filter(get_full_object_name_for_key(new_view));
  if(_use_filtered_lists)
    if(_filtered_views.find(view_name_for_filter) == _filtered_views.end())
      return;

  generate_create_stmt(new_view);

  if(strcmp(old_view->name().c_str(), new_view->name().c_str())) // name changed - need to drop old view
    generate_drop_stmt(old_view);
}

void DiffSQLGeneratorBE::generate_routine_alter_stmt(db_mysql_RoutineRef old_routine, db_mysql_RoutineRef new_routine, const grt::DiffChange *diffchange)
{
  std::string routine_name_for_filter(get_full_object_name_for_key(new_routine));
  if(_use_filtered_lists)
    if(_filtered_routines.find(routine_name_for_filter) == _filtered_routines.end())
      return;

  generate_drop_stmt(old_routine);

  generate_create_stmt(new_routine); 
}

void DiffSQLGeneratorBE::generate_alter_stmt(db_mysql_SchemaRef schema, const grt::DiffChange *diffchange)
{
  bool process_alter_schema= true;
  std::string schema_name_for_filter(get_full_object_name_for_key(schema));

  if(_use_filtered_lists)
    if(_filtered_schemata.find(schema_name_for_filter) == _filtered_schemata.end())
      process_alter_schema= false;

  std::string alter_sql;
  const grt::ChangeSet *cs= diffchange->subchanges();

  callback->alter_schema_props_begin(schema);

  if(process_alter_schema)
  {
    for(grt::ChangeSet::const_iterator e= cs->end(), it= cs->begin(); it != e; it++)
    {
      const grt::ObjectAttrModifiedChange *attr_change= static_cast<const grt::ObjectAttrModifiedChange *>(*it);

      if(attr_change->get_attr_name().compare("name") == 0)
      {
        const grt::SimpleValueChange *change= 
          static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange());
        grt::StringRef str= grt::StringRef::cast_from(change->get_new_value());
        callback->alter_schema_name(schema, str);
      }
      if(attr_change->get_attr_name().compare("defaultCharacterSetName") == 0)
      {
        grt::ValueRef value= static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange())->get_new_value();
        callback->alter_schema_default_charset(schema, grt::StringRef::cast_from(value));
      }
      else if(attr_change->get_attr_name().compare("defaultCollationName") == 0)
      {
        grt::ValueRef value= static_cast<const grt::SimpleValueChange *>(attr_change->get_subchange())->get_new_value();
        callback->alter_schema_default_collate(schema, grt::StringRef::cast_from(value));
      }
    }
  }

  callback->alter_schema_props_end(schema);

  for(grt::ChangeSet::const_iterator e= cs->end(), it= cs->begin(); it != e; it++)
  {
    const grt::ObjectAttrModifiedChange *attr_change= static_cast<const grt::ObjectAttrModifiedChange *>(*it);
    if(attr_change->get_attr_name().compare("tables") == 0)
    {
      const grt::ListChange *list_change= static_cast<const grt::ListChange *>(attr_change->get_subchange());
      const grt::ChangeSet *tables_cs= list_change->subchanges();
      for(grt::ChangeSet::const_iterator e2= tables_cs->end(), jt= tables_cs->begin(); jt != e2; jt++)
      {
        const grt::DiffChange *table_change= *jt;
        switch(table_change->get_change_type())
        {
        case grt::ListItemAdded:
          generate_create_stmt(
            db_mysql_TableRef::cast_from(static_cast<const grt::ListItemAddedChange *>(table_change)->get_added_object()));
          break;
        case grt::ListItemRemoved:
          generate_drop_stmt(schema->tables().get(
            static_cast<const grt::ListItemRemovedChange *>(table_change)->get_source_object_index()));
          break;
        case grt::ListItemModified:
          generate_alter_stmt(schema->tables().get(
            static_cast<const grt::ListItemModifiedChange *>(table_change)->get_source_object_index()), 
            *table_change->subchanges()->begin());
          break;
        case grt::ListItemOrderChanged:
          {
            const grt::ListItemOrderChange *oc= 
              static_cast<const grt::ListItemOrderChange *>(table_change);
            const grt::ListItemModifiedChange *mc= NULL;
            if(!oc->subchanges()->empty())
              mc= static_cast<const grt::ListItemModifiedChange *>(*oc->subchanges()->begin());
            if(mc)
              generate_alter_stmt(schema->tables().get(
                mc->get_source_object_index()), *mc->subchanges()->begin());
          }
          break;
          default: 
            break;
        }
      }
    }
    else if(attr_change->get_attr_name().compare("views") == 0)
    {
      const grt::ListChange *list_change= static_cast<const grt::ListChange *>(attr_change->get_subchange());
      const grt::ChangeSet *views_cs= list_change->subchanges();
      for(grt::ChangeSet::const_iterator e2= views_cs->end(), jt= views_cs->begin(); jt != e2; jt++)
      {
        const grt::DiffChange *view_change= *jt;
        switch(view_change->get_change_type())
        {
        case grt::ListItemAdded:
          {
            db_mysql_ViewRef new_view= db_mysql_ViewRef::cast_from(
              static_cast<const grt::ListItemAddedChange *>(view_change)->get_added_object());
            generate_create_stmt(new_view);
          }
          break;
        case grt::ListItemRemoved:
          {
            int idx= static_cast<const grt::ListItemRemovedChange *>(view_change)->get_source_object_index();
            generate_drop_stmt(schema->views().get(idx));
          }
          break;
        case grt::ListItemModified:
          {
            db_mysql_ViewRef old_view= db_mysql_ViewRef::cast_from(
              grt::ValueRef(static_cast<const grt::ListItemModifiedChange *>(view_change)->get_action().first.first));
            db_mysql_ViewRef new_view= db_mysql_ViewRef::cast_from(
              grt::ValueRef(static_cast<const grt::ListItemModifiedChange *>(view_change)->get_action().second.first));
            generate_alter_stmt(old_view, new_view, view_change);
          }
          break;
        // list item position change is not relevant        
          default: break;
        }
      }
    }
    else if(attr_change->get_attr_name().compare("routines") == 0)
    {
      const grt::ListChange *list_change= static_cast<const grt::ListChange *>(attr_change->get_subchange());
      const grt::ChangeSet *routines_cs= list_change->subchanges();
      for(grt::ChangeSet::const_iterator e2= routines_cs->end(), jt= routines_cs->begin(); jt != e2; jt++)
      {
        const grt::DiffChange *routine_change= *jt;
        switch(routine_change->get_change_type())
        {
        case grt::ListItemAdded:
          {
            db_mysql_RoutineRef new_routine= db_mysql_RoutineRef::cast_from(
              static_cast<const grt::ListItemAddedChange *>(routine_change)->get_added_object());
            generate_create_stmt(new_routine);
          }
          break;
        case grt::ListItemRemoved:
          {
            int idx= static_cast<const grt::ListItemRemovedChange *>(routine_change)->get_source_object_index();
            generate_drop_stmt(schema->routines().get(idx));
          }
          break;
        case grt::ListItemModified:
          {
            db_mysql_RoutineRef old_routine= db_mysql_RoutineRef::cast_from(
              grt::ValueRef(static_cast<const grt::ListItemModifiedChange *>(routine_change)->get_action().first.first));
            db_mysql_RoutineRef new_routine= db_mysql_RoutineRef::cast_from(
              grt::ValueRef(static_cast<const grt::ListItemModifiedChange *>(routine_change)->get_action().second.first));
            generate_routine_alter_stmt(old_routine, new_routine, routine_change);
          }
          break;
        // list item position change is not relevant
          default: break;        
        }
      }
    }
  }
}


void DiffSQLGeneratorBE::generate_alter_stmt(db_mysql_CatalogRef catalog, const grt::DiffChange *diffchange)
{
  // process changes in schemata
  for(grt::ChangeSet::const_iterator e= diffchange->subchanges()->end(),
    it= diffchange->subchanges()->begin(); it != e; it++)
  {
    const grt::DiffChange *subchange= *it;
    if(subchange->get_change_type() == grt::ObjectAttrModified)
    {
      const grt::ObjectAttrModifiedChange *objattrchange= 
        static_cast<const grt::ObjectAttrModifiedChange *>(subchange);

      if(objattrchange->get_attr_name().compare("schemata") == 0)
      {
        const grt::DiffChange *objattr_subchange= objattrchange->get_subchange();
        if(objattr_subchange->get_change_type() == grt::ListModified)
        {
          const grt::ListChange *schemata_list_change=
            static_cast<const grt::ListChange *>(objattr_subchange);

          for(grt::ChangeSet::const_iterator schemata_e= schemata_list_change->subchanges()->end(),
            schemata_it= schemata_list_change->subchanges()->begin(); schemata_it != schemata_e; schemata_it++)
          {
            const grt::DiffChange *schema_subchange= *schemata_it;
            switch(schema_subchange->get_change_type())
            {
            case grt::ListItemAdded:
              generate_create_stmt(
                db_mysql_SchemaRef::cast_from(static_cast<const grt::ListItemAddedChange *>(schema_subchange)->get_added_object()));
              break;
            case grt::ListItemRemoved:
              generate_drop_stmt(catalog->schemata().get(
                static_cast<const grt::ListItemRemovedChange *>(schema_subchange)->get_source_object_index()));
              break;
            case grt::ListItemModified:
              generate_alter_stmt(catalog->schemata().get(
                static_cast<const grt::ListItemModifiedChange *>(schema_subchange)->get_source_object_index()), *schema_subchange->subchanges()->begin());
              break;
            case grt::ListItemOrderChanged:
              {
                const grt::ListItemOrderChange *oc= 
                  static_cast<const grt::ListItemOrderChange *>(schema_subchange);
                const grt::ListItemModifiedChange *mc= NULL;
                if(!oc->subchanges()->empty())
                  mc= static_cast<const grt::ListItemModifiedChange *>(*oc->subchanges()->begin());
                if(mc)
                  generate_alter_stmt(catalog->schemata().get(
                    mc->get_source_object_index()), *mc->subchanges()->begin());
              }
              break;
              default: 
                break;
            }
          }
        }
      }
    }
  }
}

static void fill_set_from_list(grt::StringListRef string_list, std::set<std::string>& string_set)
{
  for(size_t count= string_list.count(), i= 0; i < count; i++)
    string_set.insert(std::string(string_list.get(i).c_str()));
}
bool set_optioin(grt::DictRef options,const char* name, const bool default_value = false)
{
    if(options.has_key(name)) 
    { 
      grt::IntegerRef opt = grt::IntegerRef::cast_from(options.get("UseFilteredLists")); 
      if(opt.is_valid()) 
        return (opt.operator long() != 0); 
    } 
    return default_value;
}

DiffSQLGeneratorBE::DiffSQLGeneratorBE(grt::DictRef options, DiffSQLGeneratorBEActionInterface *cb)
  : padding(2), callback(cb), _gen_create_index(false), _use_filtered_lists(true), _skip_foreign_keys(false)
{
  if(options.is_valid())
  {
    if(options.has_key("SkipForeignKeys")) 
    { 
      grt::IntegerRef SkipForeignKeys= grt::IntegerRef::cast_from(options.get("SkipForeignKeys")); 
      if(SkipForeignKeys.is_valid()) 
        _skip_foreign_keys = (SkipForeignKeys.operator long() != 0); 
    } 

    if(options.has_key("UseFilteredLists"))
    {
      grt::IntegerRef use_filtered_lists= grt::IntegerRef::cast_from(options.get("UseFilteredLists"));
      if(use_filtered_lists.is_valid())
        _use_filtered_lists= (use_filtered_lists.operator long() != 0);
    }
	cb->set_short_names(false);
    if(options.has_key("UseShortNames"))
    {
      grt::IntegerRef use_short_names = grt::IntegerRef::cast_from(options.get("UseShortNames"));
      if(use_short_names.is_valid())
        cb->set_short_names(use_short_names.operator long() != 0);
    }
    if(options.has_key("GenerateCreateIndex"))
    {
      grt::IntegerRef gen_index_value= grt::IntegerRef::cast_from(options.get("GenerateCreateIndex"));
      if(gen_index_value.is_valid())
        _gen_create_index= (gen_index_value.operator long() != 0);
    }

    if(options.has_key("UserFilterList"))
    {
      grt::StringListRef user_list= grt::StringListRef::cast_from(options.get("UserFilterList"));
      if(user_list.is_valid())
        fill_set_from_list(user_list, _filtered_users);
    }

    if(options.has_key("SchemaFilterList"))
    {
      grt::StringListRef schemata_list= grt::StringListRef::cast_from(options.get("SchemaFilterList"));
      if(schemata_list.is_valid())
        fill_set_from_list(schemata_list, _filtered_schemata);
    }

    if(options.has_key("TableFilterList"))
    {
      grt::StringListRef tables_list= grt::StringListRef::cast_from(options.get("TableFilterList"));
      if(tables_list.is_valid())
        fill_set_from_list(tables_list, _filtered_tables);
    }

    if(options.has_key("ViewFilterList"))
    {
      grt::StringListRef views_list= grt::StringListRef::cast_from(options.get("ViewFilterList"));
      if(views_list.is_valid())
        fill_set_from_list(views_list, _filtered_views);
    }

    if(options.has_key("RoutineFilterList"))
    {
      grt::StringListRef routines_list= grt::StringListRef::cast_from(options.get("RoutineFilterList"));
      if(routines_list.is_valid())
        fill_set_from_list(routines_list, _filtered_routines);
    }

    if(options.has_key("TriggerFilterList"))
    {
      grt::StringListRef triggers_list= grt::StringListRef::cast_from(options.get("TriggerFilterList"));
      if(triggers_list.is_valid())
        fill_set_from_list(triggers_list, _filtered_triggers);
    }
  }
}

void DiffSQLGeneratorBE::process_diff_change(grt::ValueRef org_object, grt::DiffChange *diff, grt::DictRef map)
{
  this->target_list= grt::StringListRef();
  this->target_map= map;

  do_process_diff_change(org_object, diff);
}

void DiffSQLGeneratorBE::process_diff_change(grt::ValueRef org_object, 
                                             grt::DiffChange *diff, 
                                             grt::StringListRef list, 
                                             grt::ListRef<GrtNamedObject> objlist)
{
  this->target_map= grt::DictRef();
  this->target_list= list;
  this->target_object_list= objlist;

  do_process_diff_change(org_object, diff);
}

void DiffSQLGeneratorBE::do_process_diff_change(grt::ValueRef org_object, grt::DiffChange *diff)
{
  switch(diff->get_change_type())
  {
  //case SimpleValue:
  case grt::ValueAdded:
  case grt::ListItemAdded:
  case grt::DictItemAdded:
    diff->apply(org_object);
    generate_create_stmt(db_mysql_CatalogRef::cast_from(org_object));
    break;

  case grt::ValueRemoved:
  case grt::ListItemRemoved:
  case grt::DictItemRemoved:
    generate_drop_stmt(db_mysql_CatalogRef::cast_from(org_object));
    break;

  case grt::ObjectModified:
  case grt::ObjectAttrModified:
  case grt::ListModified:
  case grt::ListItemModified:
  case grt::ListItemOrderChanged:
  case grt::DictModified:
  case grt::DictItemModified:
    generate_alter_stmt(db_mysql_CatalogRef::cast_from(org_object), diff);
    break;
  
  default:
    break;
  }
}
