/* 
 * © 2007-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include "stdafx.h"

#ifndef _WIN32
#include <pcre.h>
#include <stdio.h>
#endif

//XXX should remove this mysql specific code from here --alfredo
#include <mysql.h> // needed for enum_field_types

#include "db_object_helpers.h"
#include <grtpp_undo_manager.h>
#include <grtpp_util.h>
#include "grt/common.h"
#include "grt/parse_utils.h"
#include "grts/structs.workbench.physical.h"

#ifndef _WIN32
#include <string.h>
#endif

using namespace bec;
using namespace grt;


template<class T>
class auto_array_ptr
{
  T *_ptr;

public:
  auto_array_ptr(T* ptr) : _ptr(ptr) {}
  ~auto_array_ptr() { delete[] _ptr; }

  operator T* () { return _ptr; }
};


db_mgmt_RdbmsRef get_rdbms_for_db_object(const ::grt::ValueRef &object)
{
  GrtObjectRef parent= GrtObjectRef::cast_from(object);
  while (parent.is_valid() && !parent.is_instance("workbench.physical.Model"))
    parent= parent->owner();

  // do it the hard way to avoid havig to link to objimpl for model
  //return workbench_physical_ModelRef::cast_from(parent)->rdbms();

  if (parent.is_valid())
    return db_mgmt_RdbmsRef::cast_from(parent.get_member("rdbms"));
  return db_mgmt_RdbmsRef();
}

db_SimpleDatatypeRef CatalogHelper::get_datatype(grt::ListRef<db_SimpleDatatype> types, const std::string &name)
{
  for (size_t c= types.count(), i= 0; i < c; i++)
  {
    if (g_strcasecmp(types[i]->name().c_str(), name.c_str())==0)
      return types[i];
    //for (size_t d= types[i]->synonyms().count(), j= 0; j < d; j++)
    //{
    //  if (g_strcasecmp(types[i]->synonyms().get(j).c_str(), name.c_str())==0)
    //    return types[i];
    //}
  }

  return db_SimpleDatatypeRef();
}


std::string CatalogHelper::dbobject_to_dragdata(const db_DatabaseObjectRef &object)
{
  return object.class_name()+":"+object.id();
}


db_DatabaseObjectRef CatalogHelper::dragdata_to_dbobject(const db_CatalogRef &catalog, const std::string &data)
{
  if (data.find(':') != std::string::npos)
  {
    std::string oid= data.substr(data.find(':')+1);
    
    return db_DatabaseObjectRef::cast_from(find_child_object(catalog, oid));
  }
  return db_DatabaseObjectRef();
}


std::string CatalogHelper::dbobject_list_to_dragdata(const std::list<db_DatabaseObjectRef> &objects)
{
  std::string ret;
  for (std::list<db_DatabaseObjectRef>::const_iterator iter= objects.begin(); iter != objects.end(); ++iter)
  {
    if (!ret.empty())
      ret.append("\n");
    ret.append(dbobject_to_dragdata(*iter));
  }
  return ret;
}


std::list<db_DatabaseObjectRef> CatalogHelper::dragdata_to_dbobject_list(const db_CatalogRef &catalog, const std::string &data)
{
  std::list<db_DatabaseObjectRef> dbobjects;
  std::vector<std::string> items= split_string(data, "\n");
  for (std::vector<std::string>::const_iterator item= items.begin(); item != items.end(); ++item)
  {
    db_DatabaseObjectRef object= dragdata_to_dbobject(catalog, *item);
    if (object.is_valid())
      dbobjects.push_back(object);
  }

  return dbobjects;
}

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

std::string SchemaHelper::get_unique_foreign_key_name(const db_SchemaRef &schema, 
                                                      const std::string &prefix_,
                                                      int maxlength)
{
  std::set<std::string> used_names;
  std::string prefix;
  std::string the_name= prefix_;
  int index= 0;
  
  // Truncate if too long. On byte count, because that's what matters when creating
  // stuff on the server.
  if ((maxlength > 2) && ((int) the_name.size() > maxlength - 2))
  {
    const char *start= the_name.c_str();
    const char *end= the_name.c_str() + maxlength - 2;
      
    the_name= the_name.substr(0, g_utf8_find_prev_char(start, end) - start);
  }
  
  prefix= the_name;

  GRTLIST_FOREACH(db_Table, schema->tables(), table)
  {
    GRTLIST_FOREACH(db_ForeignKey, (*table)->foreignKeys(), fk)
    {
      used_names.insert((*fk)->name());
      if (the_name == prefix && index == 0)
        index++;
    }
  }
  
  // prefix is duplicated
  if (index > 0)
  {
    do 
    {
      the_name= strfmt("%s%i", prefix.c_str(), index++);
    } while (used_names.find(the_name) != used_names.end());
  }
  
  return the_name;
}

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

db_TableRef TableHelper::create_associative_table(const db_SchemaRef &schema, 
                                                  const db_TableRef &table1, const db_TableRef &table2, 
                                                  bool mandatory1, bool mandatory2,
                                                  const db_mgmt_RdbmsRef &rdbms,
                                                  const grt::DictRef &global_options,
                                                  const grt::DictRef &options)
{
  db_TableRef atable;
  std::string name;

  AutoUndo undo(schema->get_grt());

  name= options.get_string("AuxTableTemplate", global_options.get_string("AuxTableTemplate", "%stable%_%dtable%"));

  name= replace_variable(name, "%stable%", table1->name().c_str());
  name= replace_variable(name, "%dtable%", table2->name().c_str());

  atable= schema.get_grt()->create_object<db_Table>(table1.get_metaclass()->name());
  atable->owner(schema);
  atable->name(get_name_suggestion_for_list_object(schema->tables(), name, false));
  atable->oldName(atable->name());
  
  if (atable.has_member("tableEngine"))
    atable->set_member("tableEngine", table1.get_member("tableEngine"));
  if (atable.has_member("defaultCharacterSetName"))
    atable->set_member("defaultCharacterSetName", table1.get_member("defaultCharacterSetName"));
  if (atable.has_member("defaultCollationName"))
    atable->set_member("defaultCollationName", table1.get_member("defaultCollationName"));

  create_foreign_key_to_table(atable, table1, true, mandatory1, true, true, rdbms, global_options, options);
  schema->tables().insert(atable);//Insert put newly added FK into schema to avoid name collision
  create_foreign_key_to_table(atable, table2, true, mandatory2, true, true, rdbms, global_options, options);
  
  undo.end("Create Associative Table");

  return atable;
}


static std::string format_ident_with_table(const std::string &fmt, const db_TableRef &table)
{
  return replace_variable(fmt, "%table%", table->name().c_str());
}

static std::string format_ident_with_stable_dtable(const std::string &fmt, const db_TableRef &stable, const db_TableRef &dtable)
{
  return replace_variable(replace_variable(fmt, "%stable%", stable->name().c_str()), "%dtable%", dtable->name().c_str());
}

static std::string format_ident_with_column(const std::string &fmt, const db_ColumnRef &column)
{
  return replace_variable(replace_variable(fmt, "%table%", db_TableRef::cast_from(column->owner())->name().c_str()),
                        "%column%", column->name().c_str());
}


static db_IndexRef create_index_for_fk(grt::GRT *grt, const db_ForeignKeyRef &fk)
{
  std::string index_name(fk->name().c_str());
  //index_name.append("_index");

  // add the corresponding index
  db_IndexRef index= grt->create_object<db_Index>(db_TableRef::cast_from(fk->owner()).get_metaclass()->get_member_type("indices").content.object_class.c_str());
  
  index->owner(fk->owner());
  index->name(index_name);
  index->oldName(fk->oldName());
  index->indexType("FOREIGN");

  for(size_t i= 0, sz= fk->columns().count(); i < sz; i++)
  {
    db_ColumnRef col(fk->columns().get(i));

    db_IndexColumnRef index_col(grt->create_object<db_IndexColumn>(index.get_metaclass()->get_member_type("columns").content.object_class));
    index_col->owner(index);
//    "name", col->name().valueptr(),
    index_col->descend(grt::IntegerRef(0));
    //"columnLength", grt::IntegerRef(col->length().valueptr()).valueptr(),
    index_col->columnLength(grt::IntegerRef(0));
    index_col->referencedColumn(col);

    index->columns().insert(index_col);
  }

  return index;
}


void TableHelper::update_foreign_keys_from_column_notnull(const db_TableRef &table, const db_ColumnRef &column)
{
  AutoUndo undo(table->get_grt());

  // go through all foreign keys and update the ones that have this column
  grt::ListRef<db_ForeignKey> fklist(table->foreignKeys());

  for (size_t c= fklist.count(), i= 0; i < c; i++)
  {
    db_ForeignKeyRef fk(fklist[i]);
    size_t notnull= 0;
    bool flag= false;

    for (size_t d= fk->columns().count(), j= 0; j < d; j++)
    {
      db_ColumnRef col(fk->columns()[j]);

      if (*col->isNotNull())
        notnull++;

      if (col == column)
        flag= true;
    }
    if (flag)
    {
      if (notnull == fk->columns().count())
      {
        // if the FK is optional, then it means the ref table is optional
        fk->referencedMandatory(1);
      }
      else if (notnull == 0)
      {
        fk->referencedMandatory(0);
      }
    }
  }

  undo.end("Update FK Mandatory Flag");
}


std::string TableHelper::generate_foreign_key_name()
{
  return std::string("fk_") + grt::get_guid();
}


db_ForeignKeyRef TableHelper::create_empty_foreign_key(grt::GRT *grt, const db_TableRef &table, const std::string &name)
{
  db_ForeignKeyRef fk;

  // create a new FK
  fk= grt->create_object<db_ForeignKey>(table.get_metaclass()->get_member_type("foreignKeys").content.object_class);
  fk->owner(table);
  fk->name(name.empty()?generate_foreign_key_name():name);

  grt::AutoUndo undo(grt);
  table->foreignKeys().insert(fk);

  db_IndexRef index(create_index_for_fk(grt, fk));

  table->indices().insert(index);

  undo.end(_("Create Foreign Key"));

  return fk;
}


void TableHelper::update_foreign_key_index(grt::GRT *grt, const db_ForeignKeyRef &fk)
{
  //! todo: add undo group

  db_IndexRef index;
  db_TableRef table(db_TableRef::cast_from(fk->owner()));
  ListRef<db_Index> indices= table->indices();
  for (size_t n= 0, count= indices.count(); n < count; ++n)
  {
    db_IndexRef ind(indices.get(n));
    if ((0 == (*ind->indexType()).compare("FOREIGN")) && (ind->name() == fk->name()))
    {
      index= ind;
      break;
    }
  }

  if (!index.is_valid())
    return;

  // recreate index columns
  while (index->columns().count() > 0)
    index->columns().remove(0);

  ListRef<db_Column> fk_columns(fk->columns());
  for (size_t n= 0, count= fk_columns.count(); n < count; ++n)
  {
    db_ColumnRef column(fk_columns.get(n));
    db_IndexColumnRef index_column(grt->create_object<db_IndexColumn>(index.get_metaclass()->get_member_type("columns").content.object_class));
    index_column->owner(index);
      //"name", column->name().valueptr(),
    index_column->referencedColumn(column);
    index->columns().insert(index_column);
  }
}


/** 
 ****************************************************************************
 * @brief Creates a foreign key in table, refering to ref_table
 *
 * 
 * Options:
 * FKNameTemplate, FKColumnNameTemplate, db_ForeignKey:deleteRule, updateRule
 * 
 * @param table the table to have the FK added to
 * @param ref_table the table that the FK refers to
 * @param mandatory if the relationship is mandatory in table
 * @param ref_mandatory if the relationship is mandatory in referenced table
 * @param many cardinality of the table in the relationship
 * @param identifying whether the rel is identifying (FK is also made PK)
 * @param options the options dictionary (see above)
 *
 * @return the created foreign key
 ****************************************************************************
 */
db_ForeignKeyRef TableHelper::create_foreign_key_to_table(const db_TableRef &table, 
                                                          const db_TableRef &ref_table,
                                                          bool mandatory, bool ref_mandatory,
                                                          bool many, bool identifying,
                                                          const db_mgmt_RdbmsRef &rdbms,
                                                          const grt::DictRef &global_options,
                                                          const grt::DictRef &options)
{
  db_ForeignKeyRef new_fk;
  db_IndexRef pk= ref_table->primaryKey();
  grt::GRT *grt= table.get_grt();
  std::string name_format;
  std::string column_name_format;
  std::string scolumn_name;
  std::string dcolumn_name;
  int max_identifier_length= rdbms->maximumIdentifierLength();
  
  // check if there is a PK
  if (!pk.is_valid() || pk->columns().count() == 0)
    return new_fk;

  grt::AutoUndo undo(grt, !table->is_global());

  name_format= options.get_string("FKNameTemplate", global_options.get_string("FKNameTemplate","FK%table%"));
  column_name_format= options.get_string("FKColumnNameTemplate", global_options.get_string("FKColumnNameTemplate", "FK%table%%column%"));

  name_format= format_ident_with_stable_dtable(name_format, table, ref_table);
  name_format= format_ident_with_column(name_format, pk->columns().get(0)->referencedColumn());

  column_name_format= format_ident_with_stable_dtable(column_name_format, table, ref_table);

  // create a new FK
  new_fk= grt->create_object<db_ForeignKey>(table.get_metaclass()->get_member_type("foreignKeys").content.object_class);
  new_fk->oldName(new_fk->name());

  new_fk->deleteRule(options.get_string("db.ForeignKey:deleteRule", global_options.get_string("db.ForeignKey:deleteRule", "NO ACTION")));
  new_fk->updateRule(options.get_string("db.ForeignKey:updateRule", global_options.get_string("db.ForeignKey:updateRule", "NO ACTION")));

  new_fk->mandatory(mandatory?1:0);
  new_fk->referencedMandatory(ref_mandatory?1:0);
  new_fk->many(many?1:0);

  new_fk->referencedTable(ref_table);

  for (size_t c= pk->columns().count(), i= 0; i < c; i++)
  {
    db_IndexColumnRef pk_column= pk->columns().get(i);
    db_ColumnRef column= pk_column->referencedColumn();
    db_ColumnRef new_fk_column;

    // create the column that will be the FK in the other table
    new_fk_column= grt->create_object<db_Column>(column.class_name());
    new_fk_column->owner(table);
    new_fk_column->name(get_name_suggestion_for_list_object(table->columns(), format_ident_with_column(column_name_format, column), false));
    new_fk_column->oldName(new_fk_column->name());
    new_fk_column->isNotNull(ref_mandatory?1:0);

    ColumnHelper::copy_column(column, new_fk_column);

    table->columns().insert(new_fk_column);

    // add the new column to the FK
    new_fk->columns().insert(new_fk_column);
    new_fk->referencedColumns().insert(column);

    if (identifying)
      table->addPrimaryKeyColumn(new_fk_column);
    
    if (scolumn_name.empty())
    {
      scolumn_name= column->name();
      dcolumn_name= new_fk_column->name();
    }
  }
  
  // substitute scolumn/dcolumn now that we know the created column name
  name_format= replace_variable(replace_variable(name_format, "%scolumn%", scolumn_name), "%dcolumn%", dcolumn_name);
  
  new_fk->name(SchemaHelper::get_unique_foreign_key_name(db_SchemaRef::cast_from(table->owner()), 
                                                         name_format, 
                                                         max_identifier_length));
  new_fk->oldName(new_fk->name());

  new_fk->owner(table); // set the owner last

  // add the FK to the source table
  table->foreignKeys().insert(new_fk);
  undo.set_description_for_last_action("Add Foreign Key to Table");

  table->indices().insert(create_index_for_fk(grt, new_fk));
  undo.set_description_for_last_action("Add Index for FK to Table");

  undo.end(_("Add Foreign Key"));
  
  return new_fk;
}



db_ForeignKeyRef TableHelper::create_foreign_key_to_table(const db_TableRef &table, const std::vector<db_ColumnRef> &columns,
                                                          const db_TableRef &ref_table, const std::vector<db_ColumnRef> &refcolumns,
                                                          bool mandatory, 
                                                          bool many,
                                                          const db_mgmt_RdbmsRef &rdbms,
                                                          const grt::DictRef &global_options,
                                                          const grt::DictRef &options)
{
  db_ForeignKeyRef new_fk;
  grt::GRT *grt= table.get_grt();
  std::string name_format;
  int max_identifier_length= rdbms->maximumIdentifierLength();
  bool ref_mandatory;

  grt::AutoUndo undo(grt, table->is_global());
  
  name_format= options.get_string("FKNameTemplate", global_options.get_string("FKNameTemplate","FK%table%"));

  name_format= format_ident_with_stable_dtable(name_format, table, ref_table);
  name_format= format_ident_with_column(name_format, refcolumns[0]);

  // create a new FK
  new_fk= grt->create_object<db_ForeignKey>(table.get_metaclass()->get_member_type("foreignKeys").content.object_class);
  new_fk->name(SchemaHelper::get_unique_foreign_key_name(db_SchemaRef::cast_from(table->owner()), 
                                                         name_format,
                                                         max_identifier_length));
  new_fk->oldName(new_fk->name());

  new_fk->deleteRule(options.get_string("db.ForeignKey:deleteRule", global_options.get_string("db.ForeignKey:deleteRule", "NO ACTION")));
  new_fk->updateRule(options.get_string("db.ForeignKey:updateRule", global_options.get_string("db.ForeignKey:updateRule", "NO ACTION")));

  new_fk->referencedTable(ref_table);
  
  new_fk->mandatory(mandatory?1:0);
  //new_fk->referencedMandatory(ref_mandatory?1:0); done later
  new_fk->many(many?1:0);

  ref_mandatory= false;
  for (size_t c= refcolumns.size(), i= 0; i < c; i++)
  {
    db_ColumnRef column(refcolumns[i]);
    db_ColumnRef fk_column(columns[i]);

    if (column->isNotNull())
      ref_mandatory= true;
    
    // add the new column to the FK
    new_fk->columns().insert(fk_column);
    new_fk->referencedColumns().insert(column);
  }
  
  new_fk->referencedMandatory(ref_mandatory?1:0);

  new_fk->owner(table);

  // add the FK to the source table
  table->foreignKeys().insert(new_fk);
  table->indices().insert(create_index_for_fk(grt, new_fk));

  undo.end(_("Add Foreign Key"));
  
  return new_fk;
}

bool TableHelper::rename_foreign_key(const db_TableRef &table, db_ForeignKeyRef &fk, const std::string &new_name)
{
  std::string old_name;

  // check if the name is already taken
  if (find_named_object_in_list(table->foreignKeys(), new_name).is_valid())
    return false;

  old_name= fk->name();

  grt::AutoUndo undo(table->get_grt());

  fk->name(new_name);

  // find index for the FK
  for (size_t c= table->indices().count(), i= 0; i < c; i++)
  {
    db_IndexRef index(table->indices().get(i));

    if (*index->indexType() == "FOREIGN" && *index->name() == old_name)
    {
      // rename index
      index->name(new_name);
      break;
    }
  }

  undo.end(_("Rename Foreign Key"));

  return true;
}


bool TableHelper::rename_index(const db_TableRef &table, db_IndexRef &index, const std::string &new_name)
{
  std::string old_name;

  // check if the name is already taken
  if (find_named_object_in_list(table->indices(), new_name).is_valid())
    return false;
  
  old_name= index->name();
  
  grt::AutoUndo undo(table->get_grt());
  
  index->name(new_name);
  
  if (index->indexType() == "FOREIGN")
  {
    // find FK for the index
    for (size_t c= table->foreignKeys().count(), i= 0; i < c; i++)
    {
      db_ForeignKeyRef fk(table->foreignKeys().get(i));
    
      if (fk->name() == old_name)
      {
        // rename FK
        fk->name(new_name);
        break;
      }
    }
  }
  
  undo.end(_("Rename Index"));
  
  return true;
}


bool TableHelper::is_identifying_foreign_key(const db_TableRef &table, const db_ForeignKeyRef &fk)
{
  // check if the fk is part of the PK
  if (table->primaryKey().is_valid())
  {
    for (size_t c= fk->columns().count(), i= 0; i < c; i++)
    {
      if (!table->isPrimaryKeyColumn(fk->columns().get(i)))
        return false;
    }
    return true;
  }
  return false;
}


db_mysql_StorageEngineRef TableHelper::get_engine_by_name(grt::GRT *grt, const std::string &name)
{
  grt::ListRef<db_mysql_StorageEngine> engines;
  Module *module= grt->get_module("DbMySQL");
  
  if (!module)
    throw std::logic_error("module DbMySQL not found");
  
  grt::BaseListRef args(grt);
  engines= grt::ListRef<db_mysql_StorageEngine>::cast_from(module->call_function("getKnownEngines", args));

  if (engines.is_valid())
  {
    for (grt::ListRef<db_mysql_StorageEngine>::const_iterator iter= engines.begin();
         iter != engines.end(); ++iter)
    {
      if ((*iter)->name() == name)
        return *iter;
    }
  }
  return db_mysql_StorageEngineRef();
}






void ColumnHelper::copy_column(const db_ColumnRef &from, db_ColumnRef &to)
{
  to->userType(from->userType());
  to->precision(from->precision());
  to->scale(from->scale());
  to->length(from->length());
  to->characterSetName(from->characterSetName());
  to->collationName(from->collationName());
  while (to->flags().count() > 0)
    to->flags().remove(0);
  for (size_t c= from->flags().count(), i= 0; i < c; i++)
    to->flags().insert(from->flags().get(i));
  to->simpleType(from->simpleType());
  to->structuredType(from->structuredType());
  to->datatypeExplicitParams(from->datatypeExplicitParams());
}


ColumnTypeCompareResult ColumnHelper::compare_column_types(const db_ColumnRef &from, const db_ColumnRef &to)
{
  std::string sfrom= from->formattedType();
  std::string sto= to->formattedType();

  if (sfrom != sto)
    return COLUMNS_TYPES_DIFFER;

  if (to->characterSetName() != from->characterSetName() )
    return COLUMNS_CHARSETS_DIFFER;
    
  if ( to->collationName() != from->collationName() )
    return COLUMNS_COLLATIONS_DIFFER;

  if (to->flags().count() != from->flags().count())
    return COLUMNS_FLAGS_DIFFER;

  for (size_t c= from->flags().count(), i= 0; i < c; i++)
    if (to->flags().get_index(from->flags().get(i)) == BaseListRef::npos)
      return COLUMNS_FLAGS_DIFFER;

  //XXX compare db specific attribs

  return COLUMNS_TYPES_EQUAL;
}


void ColumnHelper::set_default_value(db_ColumnRef column, const std::string &value)
{
  // TODO: this also should affect the isNotNull flag, but sometimes e.g. for 
  // auto-inc or timestamp columns we shouldn't touch it, need to investigate 
  // the full list of exceptions, but not sure if all these fields are still present in 5.1
  column->defaultValueIsNull(g_strcasecmp(value.c_str(), "NULL") ? 0 : 1);
  column->defaultValue(value.c_str());
}

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


//XXX the following code must be moved to db.mysql module

static enum_field_types get_mysql_type_from_string(const char *type_name)
{
  if(g_strcasecmp(type_name, "INT") == 0)
    return MYSQL_TYPE_LONG;
  if(g_strcasecmp(type_name, "TINYINT") == 0)
    return MYSQL_TYPE_TINY;
  if(g_strcasecmp(type_name, "SMALLINT") == 0)
    return MYSQL_TYPE_SHORT;
  if(g_strcasecmp(type_name, "BIGINT") == 0)
    return MYSQL_TYPE_LONGLONG;
  if(g_strcasecmp(type_name, "FLOAT") == 0)
    return MYSQL_TYPE_FLOAT;
  if(g_strcasecmp(type_name, "DOUBLE") == 0)
    return MYSQL_TYPE_DOUBLE;
  if(g_strcasecmp(type_name, "TIME") == 0)
    return MYSQL_TYPE_TIME;
  if(g_strcasecmp(type_name, "DATE") == 0)
    return MYSQL_TYPE_DATE;
  if(g_strcasecmp(type_name, "DATETIME") == 0)
    return MYSQL_TYPE_DATETIME;
  if(g_strcasecmp(type_name, "TIMESTAMP") == 0)
    return MYSQL_TYPE_TIMESTAMP;
  if(g_strcasecmp(type_name, "TEXT") == 0)
    return MYSQL_TYPE_STRING;
  if(g_strcasecmp(type_name, "CHAR") == 0)
    return MYSQL_TYPE_STRING;
  if(g_strcasecmp(type_name, "VARCHAR") == 0)
    return MYSQL_TYPE_STRING;
  if(g_strcasecmp(type_name, "BLOB") == 0)
    return MYSQL_TYPE_BLOB;
  if(g_strcasecmp(type_name, "BINARY") == 0)
    return MYSQL_TYPE_BLOB;
  if(g_strcasecmp(type_name, "VARBINARY") == 0)
    return MYSQL_TYPE_BLOB;

  return MYSQL_TYPE_NULL;
}

void CatalogHelper::apply_defaults(db_mysql_ColumnRef column)
{

  // for numeric types only
  std::map<std::string, int> def_precision_map;
  def_precision_map["INT"]= 11;
  def_precision_map["TINYINT"]= 4;
  def_precision_map["SMALLINT"]= 6;
  def_precision_map["MEDIUMINT"]= 9;
  def_precision_map["BIGINT"]= 20;
  def_precision_map["FLOAT"]= 11; // not sure about last 2
  def_precision_map["DOUBLE"]= 11;

  bool default_default_value= !column->isNotNull() && (strlen(column->defaultValue().c_str()) == 0);

  if (!column->simpleType().is_valid())
    return;

  //enum_field_types ft= get_mysql_type_from_string(column->simpleType()->name().c_str());

  std::map<std::string, int>::const_iterator prec_map_it= 
    def_precision_map.find(column->simpleType()->name().c_str());
  if(prec_map_it != def_precision_map.end())
  {
    column->length(grt::IntegerRef(-1));
    if(column->precision() == bec::EMPTY_COLUMN_PRECISION)
    {
      if(column->flags().get_index("UNSIGNED") != grt::BaseListRef::npos)
        column->precision(grt::IntegerRef(prec_map_it->second)-1);
      else 
        column->precision(grt::IntegerRef(prec_map_it->second));
    }
    if (default_default_value)
      bec::ColumnHelper::set_default_value(column, "NULL");
  }
  else if((strcmp(column->simpleType()->name().c_str(), "VARCHAR") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "CHAR") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "DECIMAL") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "BOOLEAN") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "BINARY") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "TINYBLOB") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "MEDIUMBLOB") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "BLOB") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "LONGBLOB") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "TINYTEXT") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "TEXT") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "MEDIUMTEXT") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "LONGTEXT") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "ENUM") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "SET") == 0))
  {
    if (default_default_value)
      bec::ColumnHelper::set_default_value(column, "NULL");
  }
  else if((strcmp(column->simpleType()->name().c_str(), "DATETIME") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "DATE") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "TIME") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "TIMESTAMP") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "YEAR") == 0)
    || (strcmp(column->simpleType()->name().c_str(), "BIT") == 0))
  {
    column->length(grt::IntegerRef(-1));
    if (default_default_value)
      bec::ColumnHelper::set_default_value(column, "NULL");
  }

};

void CatalogHelper::apply_defaults(db_mysql_CatalogRef cat)
{
  cat->defaultCharacterSetName("latin1");
  cat->defaultCollationName("latin1_swedish_ci");

  for(size_t i= 0, schemata_count= cat->schemata().count(); i < schemata_count; i++)
  {
    db_mysql_SchemaRef schema= cat->schemata().get(i);

    if(strlen(schema->defaultCharacterSetName().c_str()) == 0)
      schema->defaultCharacterSetName(cat->defaultCharacterSetName());

    if(strlen(schema->defaultCollationName().c_str()) == 0)
      schema->defaultCollationName(get_cs_def_collation(schema->defaultCharacterSetName().c_str()));

    for(size_t j= 0, tables_count= schema->tables().count(); j < tables_count; j++)
    {
      db_mysql_TableRef table= schema->tables().get(j);

      if(strlen(table->defaultCharacterSetName().c_str()) == 0)
        table->defaultCharacterSetName(schema->defaultCharacterSetName());

      if(strlen(table->defaultCollationName().c_str()) == 0)
        table->defaultCollationName(get_cs_def_collation(table->defaultCharacterSetName().c_str()));
      
      if(strlen(table->tableEngine().c_str()) == 0)
        table->tableEngine("InnoDB");

          for (size_t c= table->foreignKeys().count(), i= 0; i < c; i++)
          {
            db_ForeignKeyRef fk(table->foreignKeys()[i]);
            if (fk->referencedTable().is_valid())
            {
              for (size_t d= fk->referencedColumns().count(), j= 0; j < d; j++)
              {
                db_mysql_ColumnRef col = db_mysql_ColumnRef::cast_from(fk->referencedColumns().get(j));
                apply_defaults(col);
              }
            }
          }

      for(size_t k= 0, col_count= table->columns().count(); k < col_count; k++)
      {
        apply_defaults(table->columns().get(k));
      }
    }
  }
}
