/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.ambari.server.orm;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.ambari.server.configuration.Configuration.DatabaseType;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
import org.eclipse.persistence.sessions.DatabaseSession;

/**
 * Interface for schema manipulation
 * Note: IF NOT EXISTS is default for all supported DDL statements
 */
public interface DBAccessor {

  /**
   * @return database connection
   */
  Connection getConnection();

  /**
   * @return new database connection
   */
  Connection getNewConnection();

  /**
   * Wraps object name with dbms-specific quotes
   * @param name object name without quotes
   * @return quoted name
   */
  String quoteObjectName(String name);

  /**
   * Create new table
   * @param tableName
   * @param columnInfo
   * @param primaryKeyColumns
   * @throws SQLException
   */
  void createTable(String tableName, List<DBColumnInfo> columnInfo,
                          String... primaryKeyColumns) throws SQLException;

  /**
   * Create new index
   * @param indexName
   * @param tableName
   * @param columnNames
   * @throws SQLException
   */
  void createIndex(String indexName, String tableName,
                          String... columnNames) throws SQLException;

  /**
   * Create new index
   * @param indexName The name of the index to be created
   * @param tableName The database table the index to be created on
   * @param columnNames The columns included into the index
   * @param isUnique Specifies whether unique index is to be created.
   * @throws SQLException Exception in case the index creation fails.
   */
  void createIndex(String indexName, String tableName, boolean isUnique,
                   String... columnNames) throws SQLException;

  /**
   * Add foreign key for a relation
   * @param tableName
   * @param constraintName
   * @param keyColumn
   * @param referenceColumn
   * @throws SQLException
   */
  void addFKConstraint(String tableName,
                              String constraintName,
                              String keyColumn,
                              String referenceTableName,
                              String referenceColumn,
                              boolean ignoreFailure) throws SQLException;

  /**
   *
   * @param tableName
   * @param constraintName
   * @param keyColumn
   * @param referenceTableName
   * @param referenceColumn
   * @param shouldCascadeOnDelete
   * @param ignoreFailure
   * @throws SQLException
   */
  void addFKConstraint(String tableName,
                              String constraintName,
                              String keyColumn,
                              String referenceTableName,
                              String referenceColumn,
                              boolean shouldCascadeOnDelete,
                              boolean ignoreFailure) throws SQLException;

  /**
   * Add foreign key for a relation
   * @param tableName
   * @param constraintName
   * @param keyColumns
   * @param referenceTableName
   * @param referenceColumns
   * @param shouldCascadeOnDelete
   * @param ignoreFailure
   * @throws SQLException
   */
  void addFKConstraint(String tableName,
                              String constraintName,
                              String[] keyColumns,
                              String referenceTableName,
                              String[] referenceColumns,
                              boolean shouldCascadeOnDelete,
                              boolean ignoreFailure) throws SQLException;

  /**
   * Add foreign key for a relation
   * @param tableName
   * @param constraintName
   * @param keyColumns
   * @param referenceTableName
   * @param referenceColumns
   * @param ignoreFailure
   * @throws SQLException
   */
  void addFKConstraint(String tableName,
                              String constraintName,
                              String[] keyColumns,
                              String referenceTableName,
                              String[] referenceColumns,
                              boolean ignoreFailure) throws SQLException;

  /**
   * Adds a column to an existing table. This method will honor the
   * {@link DBColumnInfo#isNullable} and {@link DBColumnInfo#getDefaultValue()}
   * methods and create a new column that has a {@code DEFAULT} constraint.
   * <p/>
   * Oracle is, of course, the exception here since their syntax doesn't conform
   * to widely accepted SQL standards. Because they switch the order of the
   * {@code NULL} constraint and the {@code DEFAULT} constraint, we need to
   * create the table in a non-atomic fashion. This is because the EclipseLink
   * {@link FieldDeclaration} class hard codes the order of the constraints. In
   * the case of Oracle, we alter the nullability of the table after its
   * creation.
   * <p/>
   * No work is performed if the column already exists.
   *
   * @param tableName
   * @param columnInfo
   * @throws SQLException
   */
  void addColumn(String tableName,
                        DBColumnInfo columnInfo) throws SQLException;

  /**
   * Add unique table constraint
   * @param constraintName name of the constraint
   * @param tableName name of the table
   * @param columnNames list of columns
   * @throws SQLException
   */
  void addUniqueConstraint(String tableName, String constraintName, String... columnNames)
    throws SQLException;

  /**
   * Add unique table constraint
   * @param constraintName name of the constraint
   * @param tableName name of the table
   * @param columnNames list of columns
   * @throws SQLException
   */
  void updateUniqueConstraint(String tableName, String constraintName, String... columnNames)
      throws SQLException;

  /**
   *
   * @param tableName name of the table
   * @param constraintName name of the constraint
   * @param columnName name of the column
   * @param ignoreErrors true to ignore database errors
   * @throws SQLException
   */
  void addPKConstraint(String tableName, String constraintName,boolean ignoreErrors, String... columnName) throws SQLException;

  /**
   *
   * @param tableName name of the table
   * @param constraintName name of the constraint
   * @param columnName name of the column
   * @throws SQLException
   */
  void addPKConstraint(String tableName, String constraintName, String... columnName) throws SQLException;

  /**
   * Rename existing column
   * @param tableName
   * @param oldColumnName
   * @param columnInfo
   * @throws SQLException
   */
  void renameColumn(String tableName, String oldColumnName,
                    DBColumnInfo columnInfo) throws SQLException;

  /**
   * Alter column from existing table, only supports varchar extension <br/>
   * Use following sequence for more complex stuff: <br/>
   * <li/>{@link #addColumn(String, org.apache.ambari.server.orm.DBAccessor.DBColumnInfo)}
   * <li/>{@link #updateTable(String, String, Object, String)}
   * <li/>{@link #dropColumn(String, String)}
   * <li/>{@link #renameColumn(String, String, org.apache.ambari.server.orm.DBAccessor.DBColumnInfo)}
   * @param tableName
   * @param columnInfo
   * @throws SQLException
   */
  void alterColumn(String tableName,
                          DBColumnInfo columnInfo) throws SQLException;

  /**
   * Insert row into table
   *
   * @param tableName
   * @param columnNames
   * @param values
   * @param ignoreFailure
   * @return
   * @throws SQLException
   */
  boolean insertRow(String tableName, String[] columnNames, String[] values, boolean ignoreFailure) throws SQLException;

  /**
   * Conditionally insert row into table if it does not already exist
   *
   * @param tableName
   * @param columnNames
   * @param values
   * @param ignoreFailure
   * @return
   * @throws SQLException
   */
  boolean insertRowIfMissing(String tableName, String[] columnNames, String[] values, boolean ignoreFailure) throws SQLException;

  /**
   * Simple update operation on table
   * @param tableName
   * @param columnName
   * @param value
   * @param whereClause
   * @return
   * @throws SQLException
   */
  int updateTable(String tableName, String columnName, Object value,
                         String whereClause) throws SQLException;

  /**
   * Simple update operation on table
   *
   * @param tableName
   * @param columnNameSrc
   * @param columnNameTgt
   * @return
   * @throws SQLException
   */
  void updateTable(String tableName, DBColumnInfo columnNameSrc,
                         DBColumnInfo columnNameTgt) throws SQLException;

  /**
   * Helper method to run third party scripts like Quartz DDL
   * @param filePath
   * @throws SQLException
   */
  void executeScript(String filePath) throws SQLException, IOException;

  /**
   *
   * @param query update query
   * @return same like {@code java.sql.Statement}
   * @throws SQLException
   */
  int executeUpdate(String query) throws SQLException;

  /**
   *
   * @param query update query
   * @param ignoreErrors true to ignore errors
   * @return same like {@code java.sql.Statement}
   * @throws SQLException
   */
  int executeUpdate(String query, boolean ignoreErrors) throws SQLException;

  /**
   * Conditional ad-hoc query on DB
   * @param query
   * @param tableName
   * @param hasColumnName
   * @throws SQLException
   */
  void executeQuery(String query, String tableName, String hasColumnName) throws SQLException;

  /**
   * Execute ad-hoc query on DB.
   * @param query
   * @throws SQLException
   */
  void executeQuery(String query) throws SQLException;

  /**
   * Execute query on DB
   * @param query
   * @param ignoreFailure
   * @throws SQLException
   */
  void executeQuery(String query, boolean ignoreFailure) throws SQLException;

  /**
   * Execute prepared statements
   * @param query query to execute
   * @param arguments list of arguments for prepared statement
   * @throws SQLException
   */
  void executePreparedQuery(String query, Object...arguments) throws SQLException;

  /**
   * Execute prepared update statements
   * @param query query to execute
   * @param ignoreFailure determines if exceptions during query execution should be ignored
   * @param arguments list of arguments for prepared statement
   * @throws SQLException
   */
  void executePreparedQuery(String query, boolean ignoreFailure, Object...arguments) throws SQLException;

  /**
   * Execute prepared update statements
   * @param query query to execute
   * @param arguments list of arguments for prepared statement
   * @throws SQLException
   */
  void executePreparedUpdate(String query, Object...arguments) throws SQLException;

  /**
   * Execute prepared statements which will not ignore failures
   * @param query
   * @param ignoreFailure
   * @param arguments
   * @throws SQLException
   */
  void executePreparedUpdate(String query, boolean ignoreFailure, Object...arguments) throws SQLException;

  /**
   * Execute select {@code columnName} from {@code tableName}
   * where {@code columnNames} values = {@code values}
   *
   * @param tableName            the table name
   * @param columnName           the name of the column with the data to select
   * @param conditionColumnNames an array of column names to use in the where clause
   * @param conditionValues      an array of value to pair with the column names in conditionColumnNames
   * @param ignoreFailure        true to ignore failures executing the query; false otherwise (errors building the query will be thrown, however)
   * @return a list of integers
   * @throws SQLException
   */
  List<Integer> getIntColumnValues(String tableName, String columnName, String[] conditionColumnNames,
                                   String[] conditionValues, boolean ignoreFailure) throws SQLException;

  /**
   * Execute select {@code keyColumnName}, {@code valueColumnName} from {@code tableName}
   * where {@code columnNames} values = {@code values}
   *
   * @param tableName            the table name
   * @param keyColumnName        the name of the column with the key data to select
   * @param valueColumnName      the name of the column with the value data to select
   * @param conditionColumnNames an array of column names to use in the where clause
   * @param conditionValues      an array of value to pair with the column names in conditionColumnNames
   * @param ignoreFailure        true to ignore failures executing the query; false otherwise (errors building the query will be thrown, however)
   * @return a map of key to values
   * @throws SQLException
   */
  Map<Long, String> getKeyToStringColumnMap(String tableName, String keyColumnName, String valueColumnName,
                                            String[] conditionColumnNames, String[] conditionValues,
                                            boolean ignoreFailure) throws SQLException;

  /**
   * Drop table from schema
   * @param tableName
   * @throws SQLException
   */
  void dropTable(String tableName) throws SQLException;

  /**
   * Delete all table data
   * @param tableName
   * @throws SQLException
   */
  void truncateTable(String tableName) throws SQLException;

  /**
   * Drop a column from table
   * @param tableName
   * @param columnName
   * @throws SQLException
   */
  void dropColumn(String tableName, String columnName) throws SQLException;

  /**
   * Drop sequence
   * @param sequenceName
   * @throws SQLException
   */
  void dropSequence(String sequenceName) throws SQLException;

  /**
   * Drops a FK constraint from a table. In the case of
   * {@link DatabaseType#MYSQL}, this will also ensure that any associated
   * indexes are also dropped.
   *
   * @param tableName
   *          name of the table
   * @param constraintName
   *          name of the constraint
   * @throws SQLException
   */
  void dropFKConstraint(String tableName, String constraintName) throws SQLException;

  /**
   * Drop a PK constraint from table
   * @param tableName
   * @param constraintName name of the constraint
   * @param ignoreFailure
   * @param cascade cascade delete
   * @throws SQLException
   */
  void dropPKConstraint(String tableName, String constraintName, boolean ignoreFailure, boolean cascade) throws SQLException;

  /**
   * Drop a PK constraint from table
   * @param tableName name of the table
   * @param constraintName name of the constraint
   * @param cascade cascade delete
   * @throws SQLException
   */
  void dropPKConstraint(String tableName, String constraintName, boolean cascade) throws SQLException;

  /**
   * Drop a PK constraint from table
   * @param tableName name of the table
   * @param constraintName name of the constraint
   * @param columnName name of the column from the pk constraint
   * @param cascade cascade delete
   * @throws SQLException
   */
  void dropPKConstraint(String tableName, String constraintName, String columnName, boolean cascade) throws SQLException;

  /**
   * Drop a FK constraint from table
   * @param tableName name of the table
   * @param constraintName name of the constraint
   * @throws SQLException
   */
  void dropFKConstraint(String tableName, String constraintName, boolean ignoreFailure) throws SQLException;

  /**
   * Drop a unique constraint from table
   * @param tableName name of the table
   * @param constraintName name of the constraint
   * @param ignoreFailure
   * @throws SQLException
   */
  void dropUniqueConstraint(String tableName, String constraintName, boolean ignoreFailure) throws SQLException;

  /**
   * Drop a unique constraint from table
   * @param tableName name of the table
   * @param constraintName name of the constraint
   * @throws SQLException
   */
  void dropUniqueConstraint(String tableName, String constraintName) throws SQLException;

  /**
   * Verify if table exists by looking at metadata.
   * @param tableName name of the table
   * @return
   * @throws SQLException
   */
  boolean tableExists(String tableName) throws SQLException;

  /**
   * Verify if table has any data
   * @param tableName
   * @return
   * @throws SQLException
   */
  boolean tableHasData(String tableName) throws SQLException;

  /**
   * Verify if table already has a column defined.
   * @param tableName
   * @param columnName
   * @return
   * @throws SQLException
   */
  boolean tableHasColumn(String tableName, String columnName) throws SQLException;

  /**
   * Verify if table already has a column defined.
   * @param tableName name of the table
   * @param columnName name of the column to check
   * @return false if one from passed column names not exists
   * @throws SQLException
   */
  boolean tableHasColumn(String tableName, String... columnName) throws SQLException;

  /**
   * Verify if table has a FK constraint.
   * @param tableName
   * @param fkName
   * @return true if FK with such name exists
   * @throws SQLException
   */
  boolean tableHasForeignKey(String tableName, String fkName) throws SQLException;

  /**
   * Verify if table already has a FK constraint.
   * @param tableName
   * @param refTableName
   * @param columnName
   * @param refColumnName
   * @return true if described relation exists
   * @throws SQLException
   */
  boolean tableHasForeignKey(String tableName, String refTableName,
             String columnName, String refColumnName) throws SQLException;

  /**
   * Verify if table already has a FK constraint.
   * @param tableName
   * @param referenceTableName
   * @param keyColumns
   * @param referenceColumns
   * @return true if described relation exists
   * @throws SQLException
   */
  boolean tableHasForeignKey(String tableName, String referenceTableName, String[] keyColumns,
                             String[] referenceColumns) throws SQLException;

  /**
   * Get a new DB session
   * @return
   */
  DatabaseSession getNewDatabaseSession();


  /**
   * Table has primary key
   * @param tableName name of the table
   * @param columnName name of the constraint, could be {@code null}
   * @return true if constraint exists
   * @throws SQLException
   */
  boolean tableHasPrimaryKey(String tableName, String columnName) throws SQLException;

  /**
   * Gets list of index names from database metadata
   * @param tableName
   *            the name of the table (not {@code null}).
   * @param unique
   *            list only unique indexes (not {@code null}).
   * @return the string list of index names
   * @throws SQLException
   */
  List<String> getIndexesList(String tableName, boolean unique) throws SQLException;

  /**
   * Check if index is already in scheme
   * @param tableName
   *            the name of the table (not {@code null}).
   * @param unique
   *            list only unique indexes (not {@code null}).
   * @param indexName
   *            name of the index to check
   * @return true if index present in the schema
   */
  boolean tableHasIndex(String tableName, boolean unique, String indexName) throws SQLException;

  /**
   * Gets the column's SQL type
   *
   * @param tableName
   *          the name of the table (not {@code null}).
   * @param columnName
   *          the name of the column to retrieve type for (not {@code null}).
   * @return the integer representation of the column type
   * @throws SQLException
   */
  int getColumnType(String tableName, String columnName)
      throws SQLException;

  /**
   * Get type class of the column
   * @param tableName name of the table
   * @param columnName name of the column
   * @return type class of the column
   * @throws SQLException
   * @throws ClassNotFoundException
   */
  Class getColumnClass(String tableName, String columnName) throws SQLException, ClassNotFoundException;

  /**
   * Check if column could be nullable
   * @param tableName name of the table
   * @param columnName name of the column
   * @return true if column could be nullable
   * @throws SQLException
   */
  boolean isColumnNullable(String tableName, String columnName) throws SQLException;

  /**
   * Sets the specified column to either allow or prohibit {@code NULL}.
   *
   * @param tableName
   *          the name of the table (not {@code null}).
   * @param columnInfo
   *          the column object to get name and type of column (not {@code null}).
   * @param nullable
   *          {@code true} to indicate that the column allows {@code NULL}
   *          values, {@code false} otherwise.
   * @throws SQLException
   */
  void setColumnNullable(String tableName, DBAccessor.DBColumnInfo columnInfo, boolean nullable)
      throws SQLException;

  void setColumnNullable(String tableName, String columnName, boolean nullable)
    throws SQLException;

  /**
   * Alter column wrapper, which handle DB specific type conversion
   * @param tableName name of the table
   * @param columnName name of the column
   * @param fromType previous type
   * @param toType new desired type
   * @throws SQLException
   */
  void changeColumnType(String tableName, String columnName, Class fromType, Class toType) throws SQLException;


  /**
   * Obtain column metadata information by given table and column name
   * @param tableName name of the table
   * @param columnName name of the column
   * @return column information
   * @throws SQLException
   */
  DBColumnInfo getColumnInfo(String tableName, String columnName) throws SQLException;

  /**
   * Queries the database to determine the name of the primary key constraint on
   * the specified table. Currently, this is only implemented for
   * {@link DatabaseType#POSTGRES}, {@link DatabaseType#ORACLE} and
   * {@link DatabaseType#SQL_SERVER}. {@link DatabaseType#MYSQL} does not need
   * this since PKs can be dropped without referencing their name.
   *
   * @param tableName
   *          the name of the table to lookup the PK constraint.
   * @return the name of the PK, or {@code null} if none.
   * @throws SQLException
   */
  String getPrimaryKeyConstraintName(String tableName) throws SQLException;

  /**
   * Attempts to drop the discovered PRIMARY KEY constraint on the specified
   * table, defaulting to the specified default if not found.
   *
   * @param tableName
   *          the table to drop the PK from (not {@code null}).
   * @param defaultConstraintName
   *          the default name of the PK constraint if none is found.
   * @throws SQLException
   */
  void dropPKConstraint(String tableName, String defaultConstraintName) throws SQLException;

  /**
   * Adds a default constraint to an existing column.
   *
   * @param tableName
   *          the table where the column is defined (not {@code null}).
   * @param column
   *          the column information which contains the default value (not
   *          {@code null}).
   * @throws SQLException
   */
  void addDefaultConstraint(String tableName, DBColumnInfo column) throws SQLException;

  /**
   * Move column data from {@code sourceTableName} to {@code targetTableName} using {@code sourceIDFieldName} and
   * {@code targetIDFieldName} keys to match right rows
   *
   * @param sourceTableName
   *          the source table name
   * @param sourceColumn
   *          the source column name
   * @param sourceIDFieldName
   *          the source id key filed name matched with {@code targetIDFieldName}
   * @param targetTableName
   *          the target table name
   * @param targetColumn
   *          the target column name
   * @param targetIDFieldName
   *          the target id key name matched with {@code sourceIDFieldName}
   * @param initialValue
   *          initial value for null-contained cells
   * @throws SQLException
   */
  void moveColumnToAnotherTable(String sourceTableName, DBColumnInfo sourceColumn, String sourceIDFieldName,
       String targetTableName, DBColumnInfo targetColumn, String targetIDFieldName, Object initialValue) throws SQLException;

  /**
   * Copy column from {@code targetTable} by matching
   * table keys {@code sourceIDColumnName} and {@code targetIDColumnName}
   * and condition {@code sourceConditionFieldName} = {@code condition}
   *
   * @param sourceTableName          the source table name
   * @param sourceColumn             the source column name
   * @param sourceIDFieldName1       the source id key filed name matched with {@code targetIDFieldName1}
   * @param sourceIDFieldName2       the source id key filed name matched with {@code targetIDFieldName2}
   * @param sourceIDFieldName3       the source id key filed name matched with {@code targetIDFieldName3}
   * @param targetTableName          the target table name
   * @param targetColumn             the target column name
   * @param targetIDFieldName1       the target id key name matched with {@code sourceIDFieldName1}
   * @param targetIDFieldName2       the target id key name matched with {@code sourceIDFieldName2}
   * @param targetIDFieldName3       the target id key name matched with {@code sourceIDFieldName3}
   * @param sourceConditionFieldName source key column name which should match {@code condition}
   * @param condition                value which should match {@code sourceConditionFieldName}
   * @param initialValue             initial value for null-contained cells
   * @throws SQLException
   */
  void copyColumnToAnotherTable(String sourceTableName, DBColumnInfo sourceColumn, String sourceIDFieldName1, String sourceIDFieldName2, String sourceIDFieldName3,
                                String targetTableName, DBColumnInfo targetColumn, String targetIDFieldName1, String targetIDFieldName2, String targetIDFieldName3,
                                String sourceConditionFieldName, String condition, Object initialValue) throws SQLException;

  /**
   * Remove all rows from the table
   *
   * @param tableName name of the table
   */
  void clearTable(String tableName) throws SQLException;

  /**
   * Reset all rows with {@code value} for {@code columnName} column
   *
   * @param tableName  name of the table
   * @param columnName name of the column name to be update
   * @param value      data to use for update
   */
  void clearTableColumn(String tableName, String columnName, Object value) throws SQLException;

  enum DbType {
    ORACLE,
    MYSQL,
    POSTGRES,
    DERBY,
    H2,
    UNKNOWN
  }

  /**
   * Get type of database platform
   * @return @DbType
   */
  DbType getDbType();

  /**
   * Get database schema name
   * @return @dbSchema
   */
  String getDbSchema();

  /**
   * Capture column type
   */
  class DBColumnInfo {
    private String name;
    private Class type;
    private Integer length;
    private Object defaultValue;
    private boolean isNullable;

    private FieldTypeDefinition dbType = null;

    public DBColumnInfo(String name, Class type) {
      this(name, type, null, null, true);
    }

    public DBColumnInfo(String name, Class type, Integer length) {
      this(name, type, length, null, true);
    }

    public DBColumnInfo(String name, Class type, Integer length,
                        Object defaultValue, boolean nullable) {
      this.name = name;
      this.type = type;
      this.length = length;
      this.defaultValue = defaultValue;
      isNullable = nullable;
    }

    public DBColumnInfo(String name, FieldTypeDefinition dbType, Integer length, Object defaultValue, boolean isNullable) {
      this.name = name;
      this.length = length;
      this.isNullable = isNullable;
      this.defaultValue = defaultValue;
      this.dbType = dbType;
    }

    public DBColumnInfo(String name, FieldTypeDefinition dbType, Integer length) {
      this(name, dbType, length, null, true);
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public Class getType() {
      return type;
    }

    public void setType(Class type) {
      this.type = type;
    }

    public Integer getLength() {
      return length;
    }

    public void setLength(Integer length) {
      this.length = length;
    }

    public Object getDefaultValue() {
      return defaultValue;
    }

    public void setDefaultValue(Object defaultValue) {
      this.defaultValue = defaultValue;
    }

    public boolean isNullable() {
      return isNullable;
    }

    public void setNullable(boolean nullable) {
      isNullable = nullable;
    }

    public FieldTypeDefinition getDbType() {
      return dbType;
    }

    public void setDbType(FieldTypeDefinition dbType) {
      this.dbType = dbType;
    }

    @Override
    public int hashCode() {
      return new HashCodeBuilder(17, 37)
          .append(name)
          .append(type)
          .append(length)
          .append(isNullable)
          .append(defaultValue)
          .append(dbType)
          .toHashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      DBColumnInfo that = (DBColumnInfo) o;
      return new EqualsBuilder()
          .append(name, that.name)
          .append(type, that.type)
          .append(length, that.length)
          .append(isNullable, that.isNullable)
          .append(defaultValue, that.defaultValue)
          .append(dbType, that.dbType)
          .isEquals();
    }
  }

}
