diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/ddl/DbEntity.java b/src/main/java/com/feedzai/commons/sql/abstraction/ddl/DbEntity.java index a67d12d3..72902043 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/ddl/DbEntity.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/ddl/DbEntity.java @@ -16,6 +16,8 @@ package com.feedzai.commons.sql.abstraction.ddl; import com.feedzai.commons.sql.abstraction.dml.K; +import com.feedzai.commons.sql.abstraction.engine.AbstractTranslator; + import com.google.common.collect.ImmutableList; import java.io.Serializable; @@ -141,6 +143,21 @@ public Builder newBuilder() { .addIndexes(indexes); } + /** + * Translates the given entity table creation to the translator's dialect. + * + * @param translator The translator. + * @return The create table translation result. + */ + public String translateEntityCreation(final AbstractTranslator translator) { + return translator.translateCreateTable(this) + + translator.translatePrimaryKeysNotNull(this) + + translator.translatePrimaryKeysConstraints(this) + + translator.translateForeignKey(this) + + translator.translateCreateIndexes(this) + + translator.translateCreateSequences(this); + } + /** * Builder to create immutable {@link DbEntity} objects. */ diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/AbstractDatabaseEngine.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/AbstractDatabaseEngine.java index 153d02e5..ed4ac5a2 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/AbstractDatabaseEngine.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/AbstractDatabaseEngine.java @@ -984,6 +984,17 @@ public String translate(final Expression query) { return query.translate(); } + /** + * Translates the given entity creation to the current dialect. + * + * @param entity The entity to translate. + * @return The translation result. + */ + @Override + public String translateTableCreation(final DbEntity entity) { + return entity.translateEntityCreation(translator); + } + /** * @return The dialect being in use. */ diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/AbstractTranslator.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/AbstractTranslator.java index 7bb4c536..15d92d42 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/AbstractTranslator.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/AbstractTranslator.java @@ -17,6 +17,7 @@ import com.feedzai.commons.sql.abstraction.ddl.AlterColumn; import com.feedzai.commons.sql.abstraction.ddl.DbColumn; +import com.feedzai.commons.sql.abstraction.ddl.DbEntity; import com.feedzai.commons.sql.abstraction.ddl.DropPrimaryKey; import com.feedzai.commons.sql.abstraction.ddl.Rename; import com.feedzai.commons.sql.abstraction.dml.Between; @@ -44,6 +45,7 @@ import com.feedzai.commons.sql.abstraction.dml.dialect.SqlBuilder; import com.feedzai.commons.sql.abstraction.engine.configuration.PdbProperties; import com.google.common.base.Joiner; +import java.util.Collections; import com.google.inject.Inject; import com.google.inject.Injector; import org.apache.commons.lang3.StringUtils; @@ -631,4 +633,58 @@ protected Union rowsToUnion(final List rows) { * @return The string representation of the given object. */ public abstract String translate(StringAgg stringAgg); + + /** + * Translates the given entity table creation to the current dialect. + * + * @param entity The entity to translate. + * @return The create table translation result. + */ + public abstract String translateCreateTable(DbEntity entity); + + /** + * Translates the primary key not null constraints of the given entity table to the current dialect. + * + * @param entity The entity to translate. + * @return The primary key not null constraints translation result. + */ + public String translatePrimaryKeysNotNull(DbEntity entity) { + // usually engines don't need to specify columns as not nulls to be PK. + return ""; + } + + /** + * Translates the primary key constraints of the given entity table to the current dialect. + * + * @param entity The entity to translate. + * @return The primary key constraints translation result. + */ + public abstract String translatePrimaryKeysConstraints(DbEntity entity); + + /** + * Translates the foreign key constraints of the given entity table to the current dialect. + * + * @param entity The entity to translate. + * @return The foreign key constraints translation result. + */ + public abstract List translateForeignKey(DbEntity entity); + + /** + * Translates the index creation of the given entity table to the current dialect. + * + * @param entity The entity to translate. + * @return The index creation translation result. + */ + public abstract List translateCreateIndexes(DbEntity entity); + + /** + * Translates the sequence creation of the given entity table to the current dialect. + * + * @param entity The entity to translate. + * @return The sequence creation translation result. + */ + public List translateCreateSequences(DbEntity entity) { + // the majority of engines don't need additional SQL to create sequences. + return Collections.emptyList(); + } } diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/DatabaseEngine.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/DatabaseEngine.java index abe75a86..7d7c77b3 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/DatabaseEngine.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/DatabaseEngine.java @@ -230,6 +230,14 @@ public interface DatabaseEngine extends AutoCloseable { */ String translate(final Expression query); + /** + * Translates the given entity creation to the current dialect. + * + * @param entity The entity to translate. + * @return The translation result. + */ + String translateTableCreation(final DbEntity entity); + /** * Gets the dialect being used. * diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/CockroachDBEngine.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/CockroachDBEngine.java index e71594f9..7393b30c 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/CockroachDBEngine.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/CockroachDBEngine.java @@ -17,7 +17,6 @@ package com.feedzai.commons.sql.abstraction.engine.impl; import com.feedzai.commons.sql.abstraction.ddl.DbColumn; -import com.feedzai.commons.sql.abstraction.ddl.DbColumnConstraint; import com.feedzai.commons.sql.abstraction.ddl.DbColumnType; import com.feedzai.commons.sql.abstraction.ddl.DbEntity; import com.feedzai.commons.sql.abstraction.engine.AbstractTranslator; @@ -31,9 +30,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import static com.feedzai.commons.sql.abstraction.util.StringUtils.md5; @@ -80,54 +77,7 @@ public CockroachDBEngine(final PdbProperties properties) throws DatabaseEngineEx @Override protected void createTable(final DbEntity entity) throws DatabaseEngineException { - List createTable = new ArrayList<>(); - - createTable.add("CREATE TABLE"); - createTable.add(quotize(entity.getName())); - - // COLUMNS - List columns = new ArrayList<>(); - for (DbColumn c : entity.getColumns()) { - List column = new ArrayList<>(); - column.add(quotize(c.getName())); - column.add(translateType(c)); - - for (DbColumnConstraint cc : c.getColumnConstraints()) { - column.add(cc.translate()); - } - - if (c.isDefaultValueSet()) { - column.add("DEFAULT"); - column.add(translate(c.getDefaultValue())); - } - - columns.add(join(column, " ")); - } - createTable.add("(" + join(columns, ", ")); - // COLUMNS end - - - // PRIMARY KEY - List pks = new ArrayList<>(); - for (String pk : entity.getPkFields()) { - pks.add(quotize(pk)); - } - - if (!pks.isEmpty()) { - createTable.add(","); - - final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); - - createTable.add("CONSTRAINT"); - createTable.add(quotize(pkName)); - createTable.add("PRIMARY KEY"); - createTable.add("(" + join(pks, ", ") + ")"); - } - // PK end - - createTable.add(")"); - - final String createTableStatement = join(createTable, " "); + final String createTableStatement = translator.translateCreateTable(entity); logger.trace(createTableStatement); @@ -189,37 +139,7 @@ protected void addFks(final DbEntity entity) throws DatabaseEngineException { @Override protected void addSequences(final DbEntity entity) throws DatabaseEngineException { - for (final DbColumn column : entity.getColumns()) { - if (!column.isAutoInc()) { - continue; - } - - final String sequenceName = getQuotizedSequenceName(entity, column.getName()); - - final StringBuilder createSequence = new StringBuilder() - .append("CREATE SEQUENCE ") - .append(sequenceName) - .append(" MINVALUE 0 MAXVALUE "); - switch (column.getDbColumnType()) { - case INT: - createSequence.append(Integer.MAX_VALUE); - break; - case LONG: - createSequence.append(Long.MAX_VALUE); - break; - default: - throw new DatabaseEngineException("Auto incrementation is only supported on INT and LONG"); - } - createSequence.append(" START 1 INCREMENT 1;"); - - createSequence.append("ALTER TABLE ") - .append(quotize(entity.getName())) - .append(" ALTER COLUMN ") - .append(quotize(column.getName())) - .append(" SET DEFAULT nextval('").append(sequenceName).append("')"); - - final String statement = createSequence.toString(); - + for (final String statement : translator.translateCreateSequences(entity)) { logger.trace(statement); Statement s = null; @@ -228,7 +148,8 @@ protected void addSequences(final DbEntity entity) throws DatabaseEngineExceptio s.executeUpdate(statement); } catch (final SQLException ex) { if (ex.getSQLState().equals(NAME_ALREADY_EXISTS)) { - logger.debug(dev, "Sequence {} is already defined", sequenceName); + logger.debug(dev, "Sequence is already defined"); + logger.debug(dev, ex.getMessage()); handleOperation( new OperationFault(entity.getName(), OperationFault.Type.SEQUENCE_ALREADY_EXISTS), ex ); diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/CockroachDBTranslator.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/CockroachDBTranslator.java index 129e5399..5e70d615 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/CockroachDBTranslator.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/CockroachDBTranslator.java @@ -17,9 +17,12 @@ package com.feedzai.commons.sql.abstraction.engine.impl; import com.feedzai.commons.sql.abstraction.ddl.DbColumn; +import com.feedzai.commons.sql.abstraction.ddl.DbColumnConstraint; +import com.feedzai.commons.sql.abstraction.ddl.DbEntity; import com.feedzai.commons.sql.abstraction.dml.Cast; import com.feedzai.commons.sql.abstraction.dml.Expression; import com.feedzai.commons.sql.abstraction.dml.RepeatDelimiter; +import com.feedzai.commons.sql.abstraction.engine.DatabaseEngineException; import com.feedzai.commons.sql.abstraction.engine.DatabaseEngineRuntimeException; import com.feedzai.commons.sql.abstraction.engine.OperationNotSupportedRuntimeException; @@ -27,7 +30,10 @@ import java.util.List; import static com.feedzai.commons.sql.abstraction.engine.configuration.PdbProperties.VARCHAR_SIZE; +import static com.feedzai.commons.sql.abstraction.util.StringUtils.md5; +import static com.feedzai.commons.sql.abstraction.util.StringUtils.quotize; import static java.lang.String.format; +import static org.apache.commons.lang3.StringUtils.join; /** * Provides SQL translation for CockroachDB. @@ -126,4 +132,103 @@ public String translate(final RepeatDelimiter rd) { return join(all, delimiter); } } + + @Override + public String translateCreateTable(final DbEntity entity) { + final List createTable = new ArrayList<>(); + + createTable.add("CREATE TABLE"); + createTable.add(quotize(entity.getName())); + + // COLUMNS + final List columns = new ArrayList<>(); + for (DbColumn c : entity.getColumns()) { + final List column = new ArrayList<>(); + column.add(quotize(c.getName())); + column.add(translate(c)); + + for (DbColumnConstraint cc : c.getColumnConstraints()) { + column.add(cc.translate()); + } + + if (c.isDefaultValueSet()) { + column.add("DEFAULT"); + column.add(translate(c.getDefaultValue())); + } + + columns.add(join(column, " ")); + } + createTable.add("(" + join(columns, ", ")); + // COLUMNS end + + + // PRIMARY KEY + final List pks = new ArrayList<>(); + for (String pk : entity.getPkFields()) { + pks.add(quotize(pk)); + } + + if (!pks.isEmpty()) { + createTable.add(","); + + final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); + + createTable.add("CONSTRAINT"); + createTable.add(quotize(pkName)); + createTable.add("PRIMARY KEY"); + createTable.add("(" + join(pks, ", ") + ")"); + } + // PK end + + createTable.add(")"); + + return join(createTable, " "); + } + + @Override + public String translatePrimaryKeysConstraints(final DbEntity entity) { + // primary keys are created on table creation. + return ""; + } + + @Override + public List translateCreateIndexes(final DbEntity entity) { + final List createSeqs = new ArrayList<>(); + + for (final DbColumn column : entity.getColumns()) { + if (!column.isAutoInc()) { + continue; + } + + final String sequenceName = quotize(md5(format("%s_%s_SEQ", entity.getName(), column.getName()), + properties.getMaxIdentifierSize())); + + final StringBuilder createSequence = new StringBuilder() + .append("CREATE SEQUENCE ") + .append(sequenceName) + .append(" MINVALUE 0 MAXVALUE "); + switch (column.getDbColumnType()) { + case INT: + createSequence.append(Integer.MAX_VALUE); + break; + case LONG: + createSequence.append(Long.MAX_VALUE); + break; + default: + throw new DatabaseEngineRuntimeException("Auto incrementation is only supported on INT and LONG"); + } + createSequence.append(" START 1 INCREMENT 1;"); + + createSequence.append("ALTER TABLE ") + .append(quotize(entity.getName())) + .append(" ALTER COLUMN ") + .append(quotize(column.getName())) + .append(" SET DEFAULT nextval('").append(sequenceName).append("')"); + + final String statement = createSequence.toString(); + + createSeqs.add(statement); + } + return createSeqs; + } } \ No newline at end of file diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/DB2Engine.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/DB2Engine.java index b40fde47..8e6cbed4 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/DB2Engine.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/DB2Engine.java @@ -161,31 +161,7 @@ protected int entityToPreparedStatement(final DbEntity entity, final PreparedSta @Override protected void createTable(final DbEntity entity) throws DatabaseEngineException { - List createTable = new ArrayList<>(); - - createTable.add("CREATE TABLE"); - createTable.add(quotize(entity.getName())); - List columns = new ArrayList<>(); - for (DbColumn c : entity.getColumns()) { - List column = new ArrayList<>(); - column.add(quotize(c.getName())); - column.add(translateType(c)); - - for (DbColumnConstraint cc : c.getColumnConstraints()) { - column.add(cc.translate()); - } - - if (c.isDefaultValueSet()) { - column.add("DEFAULT"); - column.add(translate(c.getDefaultValue())); - } - - columns.add(join(column, " ")); - } - createTable.add("(" + join(columns, ", ") + ")"); - - final String createTableStatement = join(createTable, " "); - + final String createTableStatement = translator.translateCreateTable(entity); logger.trace(createTableStatement); Statement s = null; @@ -216,27 +192,9 @@ protected void addPrimaryKey(final DbEntity entity) throws DatabaseEngineExcepti return; } - List pks = new ArrayList<>(); - for (String pk : entity.getPkFields()) { - pks.add(quotize(pk)); - } - - - String alterColumnSetNotNull = alterColumnSetNotNull(entity.getName(), entity.getPkFields()); - - final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); - - List statement = new ArrayList<>(); - statement.add("ALTER TABLE"); - statement.add(quotize(entity.getName())); - statement.add("ADD CONSTRAINT"); - statement.add(quotize(pkName)); - statement.add("PRIMARY KEY"); - statement.add("(" + join(pks, ", ") + ")"); - - final String addPrimaryKey = join(statement, " "); - String reorg = reorg(entity.getName()); - + final String alterColumnSetNotNull = translator.translatePrimaryKeysNotNull(entity); + final String addPrimaryKey = translator.translatePrimaryKeysConstraints(entity); + final String reorg = reorg(entity.getName()); Statement s = null; try { @@ -291,55 +249,9 @@ private String reorg(String tableName) { return join(statement, " "); } - /** - * Generates a command to set the specified columns to enforce non nullability. - * - * @param tableName The table name. - * @param columnNames The columns. - * @return The command to perform the operation. - */ - private String alterColumnSetNotNull(String tableName, List columnNames) { - List statement = new ArrayList<>(); - statement.add("ALTER TABLE"); - statement.add(quotize(tableName)); - - for (String columnName : columnNames) { - statement.add("ALTER COLUMN"); - statement.add(quotize(columnName)); - statement.add("SET NOT NULL"); - } - - return join(statement, " "); - } - @Override protected void addIndexes(final DbEntity entity) throws DatabaseEngineException { - List indexes = entity.getIndexes(); - - for (DbIndex index : indexes) { - - - List createIndex = new ArrayList<>(); - createIndex.add("CREATE"); - if (index.isUnique()) { - createIndex.add("UNIQUE"); - } - createIndex.add("INDEX"); - - List columns = new ArrayList<>(); - List columnsForName = new ArrayList<>(); - for (String column : index.getColumns()) { - columns.add(quotize(column)); - columnsForName.add(column); - } - final String idxName = md5(format("%s_%s_IDX", entity.getName(), join(columnsForName, "_")), properties.getMaxIdentifierSize()); - createIndex.add(quotize(idxName)); - createIndex.add("ON"); - createIndex.add(quotize(entity.getName())); - createIndex.add("(" + join(columns, ", ") + ")"); - - final String statement = join(createIndex, " "); - + for (final String statement : translator.translateCreateIndexes(entity)) { logger.trace(statement); Statement s = null; @@ -348,7 +260,8 @@ protected void addIndexes(final DbEntity entity) throws DatabaseEngineException s.executeUpdate(statement); } catch (final SQLException ex) { if (ex.getMessage().startsWith(NAME_ALREADY_EXISTS)) { - logger.debug(dev, "'{}' is already defined", idxName); + logger.debug(dev, "Index is already defined"); + logger.debug(dev, ex.getMessage()); handleOperation(new OperationFault(entity.getName(), OperationFault.Type.INDEX_ALREADY_EXISTS), ex); } else { throw new DatabaseEngineException("Something went wrong handling statement", ex); @@ -367,35 +280,7 @@ protected void addIndexes(final DbEntity entity) throws DatabaseEngineException @Override protected void addSequences(DbEntity entity) throws DatabaseEngineException { - for (DbColumn column : entity.getColumns()) { - if (!column.isAutoInc()) { - continue; - } - - final String sequenceName = md5(format("%s_%s_SEQ", entity.getName(), column.getName()), properties.getMaxIdentifierSize()); - - List createSequence = new ArrayList<>(); - createSequence.add("CREATE SEQUENCE"); - createSequence.add(quotize(sequenceName)); - createSequence.add("MINVALUE 0"); - switch (column.getDbColumnType()) { - case INT: - createSequence.add("MAXVALUE"); - createSequence.add(format("%d", Integer.MAX_VALUE)); - - break; - case LONG: - createSequence.add("NO MAXVALUE"); - - break; - default: - throw new DatabaseEngineException("Auto incrementation is only supported on INT and LONG"); - } - createSequence.add("START WITH 1"); - createSequence.add("INCREMENT BY 1"); - - String statement = join(createSequence, " "); - + for (final String statement : translator.translateCreateSequences(entity)) { logger.trace(statement); Statement s = null; @@ -404,7 +289,8 @@ protected void addSequences(DbEntity entity) throws DatabaseEngineException { s.executeUpdate(statement); } catch (final SQLException ex) { if (ex.getMessage().startsWith(NAME_ALREADY_EXISTS)) { - logger.debug(dev, "'{}' is already defined", sequenceName); + logger.debug(dev, "Sequence is already defined"); + logger.debug(dev, ex.getMessage()); handleOperation(new OperationFault(entity.getName(), OperationFault.Type.SEQUENCE_ALREADY_EXISTS), ex); } else { throw new DatabaseEngineException("Something went wrong handling statement", ex); @@ -740,29 +626,9 @@ protected synchronized long doPersist(final PreparedStatement ps, @Override protected void addFks(DbEntity entity) throws DatabaseEngineException { - for (DbFk fk : entity.getFks()) { - final List quotizedLocalColumns = new ArrayList<>(); - for (String s : fk.getLocalColumns()) { - quotizedLocalColumns.add(quotize(s)); - } - - final List quotizedForeignColumns = new ArrayList<>(); - for (String s : fk.getForeignColumns()) { - quotizedForeignColumns.add(quotize(s)); - } + final List alterTables = translator.translateForeignKey(entity); - final String table = quotize(entity.getName(), translator.translateEscape()); - final String quotizedLocalColumnsSting = join(quotizedLocalColumns, ", "); - final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); - - final String alterTable = - format( - "ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", - table, - quotize(md5("FK_" + table + quotizedLocalColumnsSting + quotizedForeignColumnsString, properties.getMaxIdentifierSize())), - quotizedLocalColumnsSting, - quotize(fk.getForeignTable()), - quotizedForeignColumnsString); + for (final String alterTable : alterTables) { Statement alterTableStmt = null; Statement reorgStatement = null; diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/DB2Translator.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/DB2Translator.java index deb01938..85c9acf1 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/DB2Translator.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/DB2Translator.java @@ -18,6 +18,9 @@ import com.feedzai.commons.sql.abstraction.ddl.AlterColumn; import com.feedzai.commons.sql.abstraction.ddl.DbColumn; import com.feedzai.commons.sql.abstraction.ddl.DbColumnConstraint; +import com.feedzai.commons.sql.abstraction.ddl.DbEntity; +import com.feedzai.commons.sql.abstraction.ddl.DbFk; +import com.feedzai.commons.sql.abstraction.ddl.DbIndex; import com.feedzai.commons.sql.abstraction.ddl.DropPrimaryKey; import com.feedzai.commons.sql.abstraction.ddl.Rename; import com.feedzai.commons.sql.abstraction.dml.Cast; @@ -32,6 +35,7 @@ import com.feedzai.commons.sql.abstraction.dml.Truncate; import com.feedzai.commons.sql.abstraction.dml.View; import com.feedzai.commons.sql.abstraction.engine.AbstractTranslator; +import com.feedzai.commons.sql.abstraction.engine.DatabaseEngineException; import com.feedzai.commons.sql.abstraction.engine.DatabaseEngineRuntimeException; import com.feedzai.commons.sql.abstraction.engine.OperationNotSupportedRuntimeException; import com.feedzai.commons.sql.abstraction.util.Constants; @@ -44,8 +48,10 @@ import static com.feedzai.commons.sql.abstraction.engine.configuration.PdbProperties.MAX_BLOB_SIZE; import static com.feedzai.commons.sql.abstraction.engine.configuration.PdbProperties.VARCHAR_SIZE; +import static com.feedzai.commons.sql.abstraction.util.StringUtils.md5; import static com.feedzai.commons.sql.abstraction.util.StringUtils.quotize; import static java.lang.String.format; +import static org.apache.commons.lang3.StringUtils.join; /** * Provides SQL translation for DB2. @@ -417,4 +423,186 @@ public String translateTrue() { public String translateFalse() { return "'0'"; } + + @Override + public String translateCreateTable(final DbEntity entity) { + final List createTable = new ArrayList<>(); + + createTable.add("CREATE TABLE"); + createTable.add(quotize(entity.getName())); + final List columns = new ArrayList<>(); + for (DbColumn c : entity.getColumns()) { + final List column = new ArrayList<>(); + column.add(quotize(c.getName())); + column.add(translate(c)); + + for (DbColumnConstraint cc : c.getColumnConstraints()) { + column.add(cc.translate()); + } + + if (c.isDefaultValueSet()) { + column.add("DEFAULT"); + column.add(translate(c.getDefaultValue())); + } + + columns.add(join(column, " ")); + } + createTable.add("(" + join(columns, ", ") + ")"); + + return join(createTable, " "); + } + + @Override + public String translatePrimaryKeysNotNull(final DbEntity entity) { + return alterColumnSetNotNull(entity.getName(), entity.getPkFields()); + } + + @Override + public String translatePrimaryKeysConstraints(final DbEntity entity) { + final List pks = new ArrayList<>(); + for (final String pk : entity.getPkFields()) { + pks.add(quotize(pk)); + } + + final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); + + final List statement = new ArrayList<>(); + statement.add("ALTER TABLE"); + statement.add(quotize(entity.getName())); + statement.add("ADD CONSTRAINT"); + statement.add(quotize(pkName)); + statement.add("PRIMARY KEY"); + statement.add("(" + join(pks, ", ") + ")"); + + return join(statement, " "); + } + + @Override + public List translateForeignKey(final DbEntity entity) { + final List alterTables = new ArrayList<>(); + for (final DbFk fk : entity.getFks()) { + final List quotizedLocalColumns = new ArrayList<>(); + for (String s : fk.getLocalColumns()) { + quotizedLocalColumns.add(quotize(s)); + } + + final List quotizedForeignColumns = new ArrayList<>(); + for (String s : fk.getForeignColumns()) { + quotizedForeignColumns.add(quotize(s)); + } + + final String table = quotize(entity.getName(), translateEscape()); + final String quotizedLocalColumnsSting = join(quotizedLocalColumns, ", "); + final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); + + final String alterTable = format("ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", + table, + quotize(md5( + "FK_" + table + quotizedLocalColumnsSting + quotizedForeignColumnsString, + properties.getMaxIdentifierSize() + )), + quotizedLocalColumnsSting, + quotize(fk.getForeignTable()), + quotizedForeignColumnsString + ); + + alterTables.add(alterTable); + } + return alterTables; + } + + @Override + public List translateCreateIndexes(final DbEntity entity) { + final List indexes = entity.getIndexes(); + final List createIndexes = new ArrayList<>(); + + for (final DbIndex index : indexes) { + + final List createIndex = new ArrayList<>(); + createIndex.add("CREATE"); + if (index.isUnique()) { + createIndex.add("UNIQUE"); + } + createIndex.add("INDEX"); + + final List columns = new ArrayList<>(); + final List columnsForName = new ArrayList<>(); + for (final String column : index.getColumns()) { + columns.add(quotize(column)); + columnsForName.add(column); + } + final String idxName = md5(format("%s_%s_IDX", entity.getName(), join(columnsForName, "_")), + properties.getMaxIdentifierSize() + ); + createIndex.add(quotize(idxName)); + createIndex.add("ON"); + createIndex.add(quotize(entity.getName())); + createIndex.add("(" + join(columns, ", ") + ")"); + + final String statement = join(createIndex, " "); + createIndexes.add(statement); + } + return createIndexes; + } + + @Override + public List translateCreateSequences(final DbEntity entity) { + final List createSeqs = new ArrayList<>(); + + for (final DbColumn column : entity.getColumns()) { + if (!column.isAutoInc()) { + continue; + } + + final String sequenceName = md5(format("%s_%s_SEQ", entity.getName(), column.getName()), + properties.getMaxIdentifierSize() + ); + + final List createSequence = new ArrayList<>(); + createSequence.add("CREATE SEQUENCE"); + createSequence.add(quotize(sequenceName)); + createSequence.add("MINVALUE 0"); + switch (column.getDbColumnType()) { + case INT: + createSequence.add("MAXVALUE"); + createSequence.add(format("%d", Integer.MAX_VALUE)); + + break; + case LONG: + createSequence.add("NO MAXVALUE"); + + break; + default: + throw new DatabaseEngineRuntimeException("Auto incrementation is only supported on INT and LONG"); + } + createSequence.add("START WITH 1"); + createSequence.add("INCREMENT BY 1"); + + final String statement = join(createSequence, " "); + + createSeqs.add(statement); + } + return createSeqs; + } + + /** + * Generates a command to set the specified columns to enforce non nullability. + * + * @param tableName The table name. + * @param columnNames The columns. + * @return The command to perform the operation. + */ + private String alterColumnSetNotNull(final String tableName, final List columnNames) { + final List statement = new ArrayList<>(); + statement.add("ALTER TABLE"); + statement.add(quotize(tableName)); + + for (final String columnName : columnNames) { + statement.add("ALTER COLUMN"); + statement.add(quotize(columnName)); + statement.add("SET NOT NULL"); + } + + return join(statement, " "); + } } diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/H2Engine.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/H2Engine.java index 75a8ac60..6034eb8e 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/H2Engine.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/H2Engine.java @@ -35,7 +35,6 @@ import com.feedzai.commons.sql.abstraction.entry.EntityEntry; import java.io.StringReader; -import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -211,37 +210,7 @@ private DbEntity injectNotNullIfMissing(DbEntity entity) { protected void createTable(DbEntity entity) throws DatabaseEngineException { entity = injectNotNullIfMissing(entity); - List createTable = new ArrayList<>(); - - createTable.add("CREATE TABLE"); - createTable.add(quotize(entity.getName())); - List columns = new ArrayList<>(); - List pkFields = entity.getPkFields(); - for (DbColumn c : entity.getColumns()) { - List column = new ArrayList<>(); - column.add(quotize(c.getName())); - column.add(translateType(c)); - - // If this column is PK, it must be forced to be NOT NULL (only if it's not already...) - if (pkFields.contains(c.getName()) && !c.getColumnConstraints().contains(DbColumnConstraint.NOT_NULL)) { - // Create a NOT NULL constraint - c.getColumnConstraints().add(DbColumnConstraint.NOT_NULL); - } - - for (DbColumnConstraint cc : c.getColumnConstraints()) { - column.add(cc.translate()); - } - - if (c.isDefaultValueSet()) { - column.add("DEFAULT"); - column.add(translate(c.getDefaultValue())); - } - - columns.add(join(column, " ")); - } - createTable.add("(" + join(columns, ", ") + ")"); - - final String createTableStatement = join(createTable, " "); + final String createTableStatement = translator.translateCreateTable(entity); logger.trace(createTableStatement); @@ -273,22 +242,7 @@ protected void addPrimaryKey(final DbEntity entity) throws DatabaseEngineExcepti return; } - List pks = new ArrayList<>(); - for (String pk : entity.getPkFields()) { - pks.add(quotize(pk)); - } - - final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); - - List statement = new ArrayList<>(); - statement.add("ALTER TABLE"); - statement.add(quotize(entity.getName())); - statement.add("ADD CONSTRAINT"); - statement.add(quotize(pkName)); - statement.add("PRIMARY KEY"); - statement.add("(" + join(pks, ", ") + ")"); - - final String addPrimaryKey = join(statement, " "); + final String addPrimaryKey = translator.translatePrimaryKeysConstraints(entity); logger.trace(addPrimaryKey); @@ -316,31 +270,7 @@ protected void addPrimaryKey(final DbEntity entity) throws DatabaseEngineExcepti @Override protected void addIndexes(final DbEntity entity) throws DatabaseEngineException { - List indexes = entity.getIndexes(); - - for (DbIndex index : indexes) { - - List createIndex = new ArrayList<>(); - createIndex.add("CREATE"); - if (index.isUnique()) { - createIndex.add("UNIQUE"); - } - createIndex.add("INDEX"); - - List columns = new ArrayList<>(); - List columnsForName = new ArrayList<>(); - for (String column : index.getColumns()) { - columns.add(quotize(column)); - columnsForName.add(column); - } - final String idxName = md5(format("%s_%s_IDX", entity.getName(), join(columnsForName, "_")), properties.getMaxIdentifierSize()); - createIndex.add(quotize(idxName)); - createIndex.add("ON"); - createIndex.add(quotize(entity.getName())); - createIndex.add("(" + join(columns, ", ") + ")"); - - final String statement = join(createIndex, " "); - + for (final String statement : translator.translateCreateIndexes(entity)) { logger.trace(statement); Statement s = null; @@ -349,7 +279,8 @@ protected void addIndexes(final DbEntity entity) throws DatabaseEngineException s.executeUpdate(statement); } catch (final SQLException ex) { if (ex.getSQLState().startsWith(INDEX_ALREADY_EXISTS)) { - logger.debug(dev, "'{}' is already defined", idxName); + logger.debug(dev, "Index is already defined"); + logger.debug(dev, ex.getMessage()); handleOperation(new OperationFault(entity.getName(), OperationFault.Type.INDEX_ALREADY_EXISTS), ex); } else { throw new DatabaseEngineException("Something went wrong handling statement", ex); @@ -570,29 +501,9 @@ public boolean isStringAggDistinctCapable() { @Override protected void addFks(DbEntity entity) throws DatabaseEngineException { - for (DbFk fk : entity.getFks()) { - final List quotizedLocalColumns = new ArrayList<>(); - for (String s : fk.getLocalColumns()) { - quotizedLocalColumns.add(quotize(s)); - } - - final List quotizedForeignColumns = new ArrayList<>(); - for (String s : fk.getForeignColumns()) { - quotizedForeignColumns.add(quotize(s)); - } + final List alterTables = translator.translateForeignKey(entity); - final String table = quotize(entity.getName()); - final String quotizedLocalColumnsSting = join(quotizedLocalColumns, ", "); - final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); - - final String alterTable = - format( - "ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", - table, - quotize(md5("FK_" + table + quotizedLocalColumnsSting + quotizedForeignColumnsString, properties.getMaxIdentifierSize())), - quotizedLocalColumnsSting, - quotize(fk.getForeignTable()), - quotizedForeignColumnsString); + for (final String alterTable : alterTables) { Statement alterTableStmt = null; try { diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/H2Translator.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/H2Translator.java index 14ed07b2..0c8ad898 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/H2Translator.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/H2Translator.java @@ -28,8 +28,10 @@ import java.util.List; import static com.feedzai.commons.sql.abstraction.engine.configuration.PdbProperties.VARCHAR_SIZE; +import static com.feedzai.commons.sql.abstraction.util.StringUtils.md5; import static com.feedzai.commons.sql.abstraction.util.StringUtils.quotize; import static java.lang.String.format; +import static org.apache.commons.lang3.StringUtils.join; /** * Provides SQL translation for H2. @@ -351,4 +353,129 @@ public String translateTrue() { public String translateFalse() { return "FALSE"; } + + @Override + public String translateCreateTable(final DbEntity entity) { + final List createTable = new ArrayList<>(); + + createTable.add("CREATE TABLE"); + createTable.add(quotize(entity.getName())); + final List columns = new ArrayList<>(); + final List pkFields = entity.getPkFields(); + for (DbColumn c : entity.getColumns()) { + final List column = new ArrayList<>(); + column.add(quotize(c.getName())); + column.add(translate(c)); + + // If this column is PK, it must be forced to be NOT NULL (only if it's not already...) + if (pkFields.contains(c.getName()) && !c.getColumnConstraints().contains(DbColumnConstraint.NOT_NULL)) { + // Create a NOT NULL constraint + c.getColumnConstraints().add(DbColumnConstraint.NOT_NULL); + } + + for (DbColumnConstraint cc : c.getColumnConstraints()) { + column.add(cc.translate()); + } + + if (c.isDefaultValueSet()) { + column.add("DEFAULT"); + column.add(translate(c.getDefaultValue())); + } + + columns.add(join(column, " ")); + } + createTable.add("(" + join(columns, ", ") + ")"); + + return join(createTable, " "); + } + + @Override + public String translatePrimaryKeysConstraints(final DbEntity entity) { + final List pks = new ArrayList<>(); + for (String pk : entity.getPkFields()) { + pks.add(quotize(pk)); + } + + final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); + + final List statement = new ArrayList<>(); + statement.add("ALTER TABLE"); + statement.add(quotize(entity.getName())); + statement.add("ADD CONSTRAINT"); + statement.add(quotize(pkName)); + statement.add("PRIMARY KEY"); + statement.add("(" + join(pks, ", ") + ")"); + + return join(statement, " "); + } + + @Override + public List translateForeignKey(final DbEntity entity) { + final List alterTables = new ArrayList<>(); + + for (DbFk fk : entity.getFks()) { + final List quotizedLocalColumns = new ArrayList<>(); + for (String s : fk.getLocalColumns()) { + quotizedLocalColumns.add(quotize(s)); + } + + final List quotizedForeignColumns = new ArrayList<>(); + for (String s : fk.getForeignColumns()) { + quotizedForeignColumns.add(quotize(s)); + } + + final String table = quotize(entity.getName()); + final String quotizedLocalColumnsSting = join(quotizedLocalColumns, ", "); + final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); + + final String alterTable = + format("ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", + table, + quotize(md5("FK_" + table + quotizedLocalColumnsSting + quotizedForeignColumnsString, + properties.getMaxIdentifierSize() + )), + quotizedLocalColumnsSting, + quotize(fk.getForeignTable()), + quotizedForeignColumnsString + ); + + alterTables.add(alterTable); + } + + return alterTables; + } + + @Override + public List translateCreateIndexes(final DbEntity entity) { + final List indexes = entity.getIndexes(); + final List createIndexes = new ArrayList<>(); + + for (final DbIndex index : indexes) { + + final List createIndex = new ArrayList<>(); + createIndex.add("CREATE"); + if (index.isUnique()) { + createIndex.add("UNIQUE"); + } + createIndex.add("INDEX"); + + final List columns = new ArrayList<>(); + final List columnsForName = new ArrayList<>(); + for (final String column : index.getColumns()) { + columns.add(quotize(column)); + columnsForName.add(column); + } + + final String idxName = md5(format("%s_%s_IDX", entity.getName(), + join(columnsForName, "_")), properties.getMaxIdentifierSize()); + createIndex.add(quotize(idxName)); + createIndex.add("ON"); + createIndex.add(quotize(entity.getName())); + createIndex.add("(" + join(columns, ", ") + ")"); + + final String statement = join(createIndex, " "); + createIndexes.add(statement); + } + return createIndexes; + } } diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/MySqlEngine.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/MySqlEngine.java index 1178375b..655ab13a 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/MySqlEngine.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/MySqlEngine.java @@ -37,7 +37,6 @@ import com.feedzai.commons.sql.abstraction.entry.EntityEntry; import java.io.StringReader; -import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -180,59 +179,7 @@ protected int entityToPreparedStatement(final DbEntity entity, final PreparedSta @Override protected void createTable(final DbEntity entity) throws DatabaseEngineException { - List createTable = new ArrayList<>(); - - createTable.add("CREATE TABLE"); - createTable.add(quotize(entity.getName(), escapeCharacter())); - List columns = new ArrayList<>(); - String autoIncName = ""; - // Remember that MySQL only supports one! - int numberOfAutoIncs = 0; - for (DbColumn c : entity.getColumns()) { - List column = new ArrayList<>(); - column.add(quotize(c.getName(), escapeCharacter())); - - column.add(translateType(c)); - - /* - * In MySQL only one column can be auto incremented and it must - * be set as primary key. - */ - if (c.isAutoInc()) { - autoIncName = c.getName(); - column.add("AUTO_INCREMENT"); - numberOfAutoIncs++; - } - - for (DbColumnConstraint cc : c.getColumnConstraints()) { - column.add(cc.translate()); - } - - if (c.isDefaultValueSet()) { - column.add("DEFAULT"); - column.add(translate(c.getDefaultValue())); - } - - columns.add(join(column, " ")); - } - - if (numberOfAutoIncs > 1) { - throw new DatabaseEngineException("In MySQL you can only define one auto increment column"); - } - - String pks = ""; - if (numberOfAutoIncs == 1) { - if (entity.getPkFields().size() == 0) { - pks = ", PRIMARY KEY(" + autoIncName + ")"; - } else { - - pks = ", PRIMARY KEY(" + join(entity.getPkFields(), ", ") + ")"; - } - } - - createTable.add("(" + join(columns, ", ") + pks + ")"); - - final String createTableStatement = join(createTable, " "); + final String createTableStatement = translator.translateCreateTable(entity); logger.trace(createTableStatement); Statement s = null; @@ -270,23 +217,7 @@ protected void addPrimaryKey(final DbEntity entity) throws DatabaseEngineExcepti } } - List pks = new ArrayList<>(); - for (String pk : entity.getPkFields()) { - pks.add(quotize(pk, escapeCharacter())); - } - - final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); - - List statement = new ArrayList<>(); - statement.add("ALTER TABLE"); - statement.add(quotize(entity.getName(), escapeCharacter())); - statement.add("ADD CONSTRAINT"); - statement.add(quotize(pkName, escapeCharacter())); - statement.add("PRIMARY KEY"); - statement.add("(" + join(pks, ", ") + ")"); - - final String addPrimaryKey = join(statement, " "); - + final String addPrimaryKey = translator.translatePrimaryKeysConstraints(entity); logger.trace(addPrimaryKey); Statement s = null; @@ -317,31 +248,7 @@ protected void addIndexes(final DbEntity entity) throws DatabaseEngineException return; } - List indexes = entity.getIndexes(); - - for (DbIndex index : indexes) { - - List createIndex = new ArrayList<>(); - createIndex.add("CREATE"); - if (index.isUnique()) { - createIndex.add("UNIQUE"); - } - createIndex.add("INDEX"); - - List columns = new ArrayList<>(); - List columnsForName = new ArrayList<>(); - for (String column : index.getColumns()) { - columns.add(quotize(column, escapeCharacter())); - columnsForName.add(column); - } - final String idxName = md5(format("%s_%s_IDX", entity.getName(), join(columnsForName, "_")), properties.getMaxIdentifierSize()); - createIndex.add(quotize(idxName, escapeCharacter())); - createIndex.add("ON"); - createIndex.add(quotize(entity.getName(), escapeCharacter())); - createIndex.add("(" + join(columns, ", ") + ")"); - - final String statement = join(createIndex, " "); - + for (final String statement : translator.translateCreateIndexes(entity)) { logger.trace(statement); Statement s = null; @@ -350,7 +257,8 @@ protected void addIndexes(final DbEntity entity) throws DatabaseEngineException s.executeUpdate(statement); } catch (final SQLException ex) { if (ex.getErrorCode() == DUPLICATE_KEY_NAME) { - logger.debug(dev, "'{}' is already defined", idxName); + logger.debug(dev, "Index is already defined"); + logger.debug(dev, ex.getMessage()); handleOperation(new OperationFault(entity.getName(), OperationFault.Type.INDEX_ALREADY_EXISTS), ex); } else { throw new DatabaseEngineException("Something went wrong handling statement", ex); @@ -572,29 +480,9 @@ protected synchronized long doPersist(final PreparedStatement ps, @Override protected void addFks(DbEntity entity) throws DatabaseEngineException { - for (DbFk fk : entity.getFks()) { - final List quotizedLocalColumns = new ArrayList<>(); - for (String s : fk.getLocalColumns()) { - quotizedLocalColumns.add(quotize(s, escapeCharacter())); - } - - final List quotizedForeignColumns = new ArrayList<>(); - for (String s : fk.getForeignColumns()) { - quotizedForeignColumns.add(quotize(s, escapeCharacter())); - } + final List alterTables = translator.translateForeignKey(entity); - final String table = quotize(entity.getName(), escapeCharacter()); - final String quotizedLocalColumnsSting = join(quotizedLocalColumns, ", "); - final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); - - final String alterTable = - format( - "ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", - table, - quotize(md5("FK_" + table + quotizedLocalColumnsSting + quotizedForeignColumnsString, properties.getMaxIdentifierSize()), escapeCharacter()), - quotizedLocalColumnsSting, - quotize(fk.getForeignTable(), escapeCharacter()), - quotizedForeignColumnsString); + for (final String alterTable : alterTables) { Statement alterTableStmt = null; try { diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/MySqlTranslator.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/MySqlTranslator.java index fff0da66..2802f770 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/MySqlTranslator.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/MySqlTranslator.java @@ -30,6 +30,7 @@ import static com.feedzai.commons.sql.abstraction.dml.dialect.SqlBuilder.union; import static com.feedzai.commons.sql.abstraction.engine.configuration.PdbProperties.VARCHAR_SIZE; +import static com.feedzai.commons.sql.abstraction.util.StringUtils.md5; import static com.feedzai.commons.sql.abstraction.util.StringUtils.quotize; import static java.lang.String.format; @@ -323,4 +324,150 @@ public String translateTrue() { public String translateFalse() { return "0"; } + + @Override + public String translateCreateTable(final DbEntity entity) { + final List createTable = new ArrayList<>(); + + createTable.add("CREATE TABLE"); + createTable.add(quotize(entity.getName(), translateEscape())); + final List columns = new ArrayList<>(); + String autoIncName = ""; + // Remember that MySQL only supports one! + int numberOfAutoIncs = 0; + for (DbColumn c : entity.getColumns()) { + final List column = new ArrayList<>(); + column.add(quotize(c.getName(), translateEscape())); + + column.add(translate(c)); + + /* + * In MySQL only one column can be auto incremented and it must + * be set as primary key. + */ + if (c.isAutoInc()) { + autoIncName = c.getName(); + column.add("AUTO_INCREMENT"); + numberOfAutoIncs++; + } + + for (DbColumnConstraint cc : c.getColumnConstraints()) { + column.add(cc.translate()); + } + + if (c.isDefaultValueSet()) { + column.add("DEFAULT"); + column.add(translate(c.getDefaultValue())); + } + + columns.add(join(column, " ")); + } + + if (numberOfAutoIncs > 1) { + throw new DatabaseEngineRuntimeException("In MySQL you can only define one auto increment column"); + } + + String pks = ""; + if (numberOfAutoIncs == 1) { + if (entity.getPkFields().size() == 0) { + pks = ", PRIMARY KEY(" + autoIncName + ")"; + } else { + + pks = ", PRIMARY KEY(" + join(entity.getPkFields(), ", ") + ")"; + } + } + + createTable.add("(" + join(columns, ", ") + pks + ")"); + + return join(createTable, " "); + } + + @Override + public String translatePrimaryKeysConstraints(final DbEntity entity) { + final List pks = new ArrayList<>(); + for (String pk : entity.getPkFields()) { + pks.add(quotize(pk, translateEscape())); + } + + final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); + + final List statement = new ArrayList<>(); + statement.add("ALTER TABLE"); + statement.add(quotize(entity.getName(), translateEscape())); + statement.add("ADD CONSTRAINT"); + statement.add(quotize(pkName, translateEscape())); + statement.add("PRIMARY KEY"); + statement.add("(" + join(pks, ", ") + ")"); + + return join(statement, " "); + } + + @Override + public List translateForeignKey(final DbEntity entity) { + final List alterTables = new ArrayList<>(); + + for (final DbFk fk : entity.getFks()) { + final List quotizedLocalColumns = new ArrayList<>(); + for (String s : fk.getLocalColumns()) { + quotizedLocalColumns.add(quotize(s, translateEscape())); + } + + final List quotizedForeignColumns = new ArrayList<>(); + for (final String s : fk.getForeignColumns()) { + quotizedForeignColumns.add(quotize(s, translateEscape())); + } + + final String table = quotize(entity.getName(), translateEscape()); + final String quotizedLocalColumnsSting = join(quotizedLocalColumns, ", "); + final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); + + final String alterTable = format( + "ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", + table, + quotize(md5("FK_" + table + quotizedLocalColumnsSting + quotizedForeignColumnsString, + properties.getMaxIdentifierSize() + ), translateEscape()), + quotizedLocalColumnsSting, + quotize(fk.getForeignTable(), translateEscape()), + quotizedForeignColumnsString + ); + + alterTables.add(alterTable); + } + return alterTables; + } + + @Override + public List translateCreateIndexes(final DbEntity entity) { + final List indexes = entity.getIndexes(); + final List createIndexes = new ArrayList<>(); + + for (final DbIndex index : indexes) { + + final List createIndex = new ArrayList<>(); + createIndex.add("CREATE"); + if (index.isUnique()) { + createIndex.add("UNIQUE"); + } + createIndex.add("INDEX"); + + final List columns = new ArrayList<>(); + final List columnsForName = new ArrayList<>(); + for (final String column : index.getColumns()) { + columns.add(quotize(column, translateEscape())); + columnsForName.add(column); + } + + final String idxName = md5(format("%s_%s_IDX", entity.getName(), + join(columnsForName, "_")), properties.getMaxIdentifierSize()); + createIndex.add(quotize(idxName, translateEscape())); + createIndex.add("ON"); + createIndex.add(quotize(entity.getName(), translateEscape())); + createIndex.add("(" + join(columns, ", ") + ")"); + + final String statement = join(createIndex, " "); + createIndexes.add(statement); + } + return createIndexes; + } } diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/OracleEngine.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/OracleEngine.java index 3f4498ef..0bb72611 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/OracleEngine.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/OracleEngine.java @@ -457,31 +457,7 @@ protected void createTable(final DbEntity entity) throws DatabaseEngineException * @since 2.1.13 */ private String getCreateTableStatement(final DbEntity entity) throws DatabaseEngineException { - final List createTable = new ArrayList<>(); - - createTable.add("CREATE TABLE"); - createTable.add(quotize(entity.getName())); - final List columns = new ArrayList<>(); - for (final DbColumn c : entity.getColumns()) { - final List column = new ArrayList<>(); - column.add(quotize(c.getName())); - column.add(translateType(c)); - - if (c.isDefaultValueSet() && !c.getDbColumnType().equals(DbColumnType.BOOLEAN)) { - column.add("DEFAULT"); - column.add(translate(c.getDefaultValue())); - } - - for (final DbColumnConstraint cc : c.getColumnConstraints()) { - column.add(cc.translate()); - } - columns.add(join(column, " ")); - } - createTable.add("(" + join(columns, ", ") + ")"); - // segment creation deferred can cause unexpected behaviour in sequences - // see: http://dirknachbar.blogspot.pt/2011/01/deferred-segment-creation-under-oracle.html - createTable.add("SEGMENT CREATION IMMEDIATE"); - return join(createTable, " "); + return translator.translateCreateTable(entity); } /** @@ -512,23 +488,7 @@ protected void addPrimaryKey(final DbEntity entity) throws DatabaseEngineExcepti return; } - List pks = new ArrayList<>(); - for (String pk : entity.getPkFields()) { - pks.add(quotize(pk)); - } - - final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); - - List statement = new ArrayList<>(); - statement.add("ALTER TABLE"); - statement.add(quotize(entity.getName())); - statement.add("ADD CONSTRAINT"); - statement.add(quotize(pkName)); - statement.add("PRIMARY KEY"); - statement.add("(" + join(pks, ", ") + ")"); - - final String addPrimaryKey = join(statement, " "); - + final String addPrimaryKey = translator.translatePrimaryKeysConstraints(entity); logger.trace(addPrimaryKey); Statement s = null; @@ -555,32 +515,7 @@ protected void addPrimaryKey(final DbEntity entity) throws DatabaseEngineExcepti @Override protected void addIndexes(final DbEntity entity) throws DatabaseEngineException { - List indexes = entity.getIndexes(); - - for (DbIndex index : indexes) { - - - List createIndex = new ArrayList<>(); - createIndex.add("CREATE"); - if (index.isUnique()) { - createIndex.add("UNIQUE"); - } - createIndex.add("INDEX"); - - List columns = new ArrayList<>(); - List columnsForName = new ArrayList<>(); - for (String column : index.getColumns()) { - columns.add(quotize(column)); - columnsForName.add(column); - } - final String idxName = md5(format("%s_%s_IDX", entity.getName(), join(columnsForName, "_")), properties.getMaxIdentifierSize()); - createIndex.add(quotize(idxName)); - createIndex.add("ON"); - createIndex.add(quotize(entity.getName())); - createIndex.add("(" + join(columns, ", ") + ")"); - - final String statement = join(createIndex, " "); - + for (final String statement : translator.translateCreateIndexes(entity)) { logger.trace(statement); Statement s = null; @@ -589,7 +524,8 @@ protected void addIndexes(final DbEntity entity) throws DatabaseEngineException s.executeUpdate(statement); } catch (final SQLException ex) { if (ex.getMessage().startsWith(NAME_ALREADY_EXISTS)) { - logger.debug(dev, "'{}' is already defined", idxName); + logger.debug(dev, "Index is already defined"); + logger.debug(dev, ex.getMessage()); handleOperation(new OperationFault(entity.getName(), OperationFault.Type.INDEX_ALREADY_EXISTS), ex); } else { throw new DatabaseEngineException("Something went wrong handling statement", ex); @@ -608,35 +544,7 @@ protected void addIndexes(final DbEntity entity) throws DatabaseEngineException @Override protected void addSequences(DbEntity entity) throws DatabaseEngineException { - for (DbColumn column : entity.getColumns()) { - if (!column.isAutoInc()) { - continue; - } - - final String sequenceName = md5(format("%s_%s_SEQ", entity.getName(), column.getName()), properties.getMaxIdentifierSize()); - - List createSequence = new ArrayList<>(); - createSequence.add("CREATE SEQUENCE "); - createSequence.add(quotize(sequenceName)); - createSequence.add("MINVALUE 0"); - createSequence.add("MAXVALUE"); - switch (column.getDbColumnType()) { - case INT: - createSequence.add(format("%d", Integer.MAX_VALUE)); - - break; - case LONG: - createSequence.add(format("%d", Long.MAX_VALUE)); - - break; - default: - throw new DatabaseEngineException("Auto incrementation is only supported on INT and LONG"); - } - createSequence.add("START WITH 1"); - createSequence.add("INCREMENT BY 1"); - - String statement = join(createSequence, " "); - + for (final String statement : translator.translateCreateSequences(entity)) { logger.trace(statement); Statement s = null; @@ -645,7 +553,8 @@ protected void addSequences(DbEntity entity) throws DatabaseEngineException { s.executeUpdate(statement); } catch (final SQLException ex) { if (ex.getMessage().startsWith(NAME_ALREADY_EXISTS)) { - logger.debug(dev, "'{}' is already defined", sequenceName); + logger.debug(dev, "Sequence is already defined"); + logger.debug(dev, ex.getMessage()); handleOperation(new OperationFault(entity.getName(), OperationFault.Type.SEQUENCE_ALREADY_EXISTS), ex); } else { throw new DatabaseEngineException("Something went wrong handling statement", ex); @@ -982,32 +891,9 @@ private void silentlyExecuteStatements(List stmts) { @Override protected void addFks(DbEntity entity) throws DatabaseEngineException { - for (DbFk fk : entity.getFks()) { - final List quotizedLocalColumns = new ArrayList<>(); - for (String s : fk.getLocalColumns()) { - quotizedLocalColumns.add(quotize(s)); - } - - final List quotizedForeignColumns = new ArrayList<>(); - for (String s : fk.getForeignColumns()) { - quotizedForeignColumns.add(quotize(s)); - } + final List alterTables = translator.translateForeignKey(entity); - final String quotizedTable = quotize(entity.getName()); - final String quotizedLocalColumnsString = join(quotizedLocalColumns, ", "); - final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); - - final String alterTable = format( - "ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", - quotizedTable, - quotize(md5( - "FK_" + quotizedTable + quotizedLocalColumnsString + quotizedForeignColumnsString, - properties.getMaxIdentifierSize() - )), - quotizedLocalColumnsString, - quotize(fk.getForeignTable()), - quotizedForeignColumnsString - ); + for (final String alterTable : alterTables) { Statement alterTableStmt = null; try { diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/OracleTranslator.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/OracleTranslator.java index 8227e7c7..6070e2d4 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/OracleTranslator.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/OracleTranslator.java @@ -18,6 +18,10 @@ import com.feedzai.commons.sql.abstraction.ddl.AlterColumn; import com.feedzai.commons.sql.abstraction.ddl.DbColumn; import com.feedzai.commons.sql.abstraction.ddl.DbColumnConstraint; +import com.feedzai.commons.sql.abstraction.ddl.DbColumnType; +import com.feedzai.commons.sql.abstraction.ddl.DbEntity; +import com.feedzai.commons.sql.abstraction.ddl.DbFk; +import com.feedzai.commons.sql.abstraction.ddl.DbIndex; import com.feedzai.commons.sql.abstraction.ddl.DropPrimaryKey; import com.feedzai.commons.sql.abstraction.ddl.Rename; import com.feedzai.commons.sql.abstraction.dml.Cast; @@ -31,6 +35,7 @@ import com.feedzai.commons.sql.abstraction.dml.StringAgg; import com.feedzai.commons.sql.abstraction.dml.View; import com.feedzai.commons.sql.abstraction.engine.AbstractTranslator; +import com.feedzai.commons.sql.abstraction.engine.DatabaseEngineException; import com.feedzai.commons.sql.abstraction.engine.DatabaseEngineRuntimeException; import com.feedzai.commons.sql.abstraction.engine.OperationNotSupportedRuntimeException; import com.feedzai.commons.sql.abstraction.util.StringUtils; @@ -42,8 +47,10 @@ import java.util.stream.Collectors; import static com.feedzai.commons.sql.abstraction.engine.configuration.PdbProperties.VARCHAR_SIZE; +import static com.feedzai.commons.sql.abstraction.util.StringUtils.md5; import static com.feedzai.commons.sql.abstraction.util.StringUtils.quotize; import static java.lang.String.format; +import static org.apache.commons.lang3.StringUtils.join; /** * Provides SQL translation for Oracle. @@ -363,4 +370,164 @@ public String translateTrue() { public String translateFalse() { return "'0'"; } + + @Override + public String translateCreateTable(final DbEntity entity) { + final List createTable = new ArrayList<>(); + + createTable.add("CREATE TABLE"); + createTable.add(quotize(entity.getName())); + final List columns = new ArrayList<>(); + for (final DbColumn c : entity.getColumns()) { + final List column = new ArrayList<>(); + column.add(quotize(c.getName())); + column.add(translate(c)); + + if (c.isDefaultValueSet() && !c.getDbColumnType().equals(DbColumnType.BOOLEAN)) { + column.add("DEFAULT"); + column.add(translate(c.getDefaultValue())); + } + + for (final DbColumnConstraint cc : c.getColumnConstraints()) { + column.add(cc.translate()); + } + columns.add(join(column, " ")); + } + createTable.add("(" + join(columns, ", ") + ")"); + // segment creation deferred can cause unexpected behaviour in sequences + // see: http://dirknachbar.blogspot.pt/2011/01/deferred-segment-creation-under-oracle.html + createTable.add("SEGMENT CREATION IMMEDIATE"); + return join(createTable, " "); + } + + @Override + public String translatePrimaryKeysConstraints(final DbEntity entity) { + final List pks = new ArrayList<>(); + for (String pk : entity.getPkFields()) { + pks.add(quotize(pk)); + } + + final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); + + final List statement = new ArrayList<>(); + statement.add("ALTER TABLE"); + statement.add(quotize(entity.getName())); + statement.add("ADD CONSTRAINT"); + statement.add(quotize(pkName)); + statement.add("PRIMARY KEY"); + statement.add("(" + join(pks, ", ") + ")"); + + return join(statement, " "); + } + + @Override + public List translateForeignKey(final DbEntity entity) { + final List alterTables = new ArrayList<>(); + + for (final DbFk fk : entity.getFks()) { + final List quotizedLocalColumns = new ArrayList<>(); + for (String s : fk.getLocalColumns()) { + quotizedLocalColumns.add(quotize(s)); + } + + final List quotizedForeignColumns = new ArrayList<>(); + for (final String s : fk.getForeignColumns()) { + quotizedForeignColumns.add(quotize(s)); + } + + final String quotizedTable = quotize(entity.getName()); + final String quotizedLocalColumnsString = join(quotizedLocalColumns, ", "); + final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); + + final String alterTable = + format("ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", + quotizedTable, + quotize(md5( + "FK_" + quotizedTable + quotizedLocalColumnsString + quotizedForeignColumnsString, + properties.getMaxIdentifierSize() + )), + quotizedLocalColumnsString, + quotize(fk.getForeignTable()), + quotizedForeignColumnsString + ); + + alterTables.add(alterTable); + } + return alterTables; + } + + @Override + public List translateCreateIndexes(final DbEntity entity) { + final List indexes = entity.getIndexes(); + final List createIndexes = new ArrayList<>(); + + for (final DbIndex index : indexes) { + + + final List createIndex = new ArrayList<>(); + createIndex.add("CREATE"); + if (index.isUnique()) { + createIndex.add("UNIQUE"); + } + createIndex.add("INDEX"); + + final List columns = new ArrayList<>(); + final List columnsForName = new ArrayList<>(); + for (final String column : index.getColumns()) { + columns.add(quotize(column)); + columnsForName.add(column); + } + + final String idxName = md5(format("%s_%s_IDX", entity.getName(), + join(columnsForName, "_")), properties.getMaxIdentifierSize()); + + createIndex.add(quotize(idxName)); + createIndex.add("ON"); + createIndex.add(quotize(entity.getName())); + createIndex.add("(" + join(columns, ", ") + ")"); + + final String statement = join(createIndex, " "); + createIndexes.add(statement); + } + return createIndexes; + } + + @Override + public List translateCreateSequences(final DbEntity entity) { + final List createSeqs = new ArrayList<>(); + + for (final DbColumn column : entity.getColumns()) { + if (!column.isAutoInc()) { + continue; + } + + final String sequenceName = md5(format("%s_%s_SEQ", entity.getName(), column.getName()), + properties.getMaxIdentifierSize()); + + final List createSequence = new ArrayList<>(); + createSequence.add("CREATE SEQUENCE "); + createSequence.add(quotize(sequenceName)); + createSequence.add("MINVALUE 0"); + createSequence.add("MAXVALUE"); + switch (column.getDbColumnType()) { + case INT: + createSequence.add(format("%d", Integer.MAX_VALUE)); + + break; + case LONG: + createSequence.add(format("%d", Long.MAX_VALUE)); + + break; + default: + throw new DatabaseEngineRuntimeException("Auto incrementation is only supported on INT and LONG"); + } + createSequence.add("START WITH 1"); + createSequence.add("INCREMENT BY 1"); + + final String statement = join(createSequence, " "); + + createSeqs.add(statement); + } + return createSeqs; + } } diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/PostgreSqlEngine.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/PostgreSqlEngine.java index 83738ba7..aa042c45 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/PostgreSqlEngine.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/PostgreSqlEngine.java @@ -225,30 +225,7 @@ private Object getJSONValue(String val) throws DatabaseEngineException { @Override protected void createTable(final DbEntity entity) throws DatabaseEngineException { - List createTable = new ArrayList<>(); - - createTable.add("CREATE TABLE"); - createTable.add(quotize(entity.getName())); - List columns = new ArrayList<>(); - for (DbColumn c : entity.getColumns()) { - List column = new ArrayList<>(); - column.add(quotize(c.getName())); - column.add(translateType(c)); - - for (DbColumnConstraint cc : c.getColumnConstraints()) { - column.add(cc.translate()); - } - - if (c.isDefaultValueSet()) { - column.add("DEFAULT"); - column.add(translate(c.getDefaultValue())); - } - - columns.add(join(column, " ")); - } - createTable.add("(" + join(columns, ", ") + ")"); - - final String createTableStatement = join(createTable, " "); + final String createTableStatement = translator.translateCreateTable(entity); logger.trace(createTableStatement); @@ -280,23 +257,7 @@ protected void addPrimaryKey(final DbEntity entity) throws DatabaseEngineExcepti return; } - List pks = new ArrayList<>(); - for (String pk : entity.getPkFields()) { - pks.add(quotize(pk)); - } - - final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); - - List statement = new ArrayList<>(); - statement.add("ALTER TABLE"); - statement.add(quotize(entity.getName())); - statement.add("ADD CONSTRAINT"); - statement.add(quotize(pkName)); - statement.add("PRIMARY KEY"); - statement.add("(" + join(pks, ", ") + ")"); - - final String addPrimaryKey = join(statement, " "); - + final String addPrimaryKey = translator.translatePrimaryKeysConstraints(entity); logger.trace(addPrimaryKey); Statement s = null; @@ -323,31 +284,7 @@ protected void addPrimaryKey(final DbEntity entity) throws DatabaseEngineExcepti @Override protected void addIndexes(final DbEntity entity) throws DatabaseEngineException { - List indexes = entity.getIndexes(); - - for (DbIndex index : indexes) { - - List createIndex = new ArrayList<>(); - createIndex.add("CREATE"); - if (index.isUnique()) { - createIndex.add("UNIQUE"); - } - createIndex.add("INDEX"); - - List columns = new ArrayList<>(); - List columnsForName = new ArrayList<>(); - for (String column : index.getColumns()) { - columns.add(quotize(column)); - columnsForName.add(column); - } - final String idxName = md5(format("%s_%s_IDX", entity.getName(), join(columnsForName, "_")), properties.getMaxIdentifierSize()); - createIndex.add(quotize(idxName)); - createIndex.add("ON"); - createIndex.add(quotize(entity.getName())); - createIndex.add("(" + join(columns, ", ") + ")"); - - final String statement = join(createIndex, " "); - + for (final String statement : translator.translateCreateIndexes(entity)) { logger.trace(statement); Statement s = null; @@ -356,7 +293,8 @@ protected void addIndexes(final DbEntity entity) throws DatabaseEngineException s.executeUpdate(statement); } catch (final SQLException ex) { if (ex.getSQLState().startsWith(NAME_ALREADY_EXISTS)) { - logger.debug(dev, "'{}' is already defined", idxName); + logger.debug(dev, "Index is already defined"); + logger.debug(dev, ex.getMessage()); handleOperation(new OperationFault(entity.getName(), OperationFault.Type.INDEX_ALREADY_EXISTS), ex); } else { throw new DatabaseEngineException("Something went wrong handling statement", ex); @@ -605,29 +543,9 @@ protected void updatePersistAutoIncSequence(final MappedEntity mappedEntity, lon @Override protected void addFks(DbEntity entity) throws DatabaseEngineException { - for (DbFk fk : entity.getFks()) { - final List quotizedLocalColumns = new ArrayList<>(); - for (String s : fk.getLocalColumns()) { - quotizedLocalColumns.add(quotize(s)); - } - - final List quotizedForeignColumns = new ArrayList<>(); - for (String s : fk.getForeignColumns()) { - quotizedForeignColumns.add(quotize(s)); - } + final List alterTables = translator.translateForeignKey(entity); - final String table = quotize(entity.getName()); - final String quotizedLocalColumnsSting = join(quotizedLocalColumns, ", "); - final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); - - final String alterTable = - format( - "ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", - table, - quotize(md5("FK_" + table + quotizedLocalColumnsSting + quotizedForeignColumnsString, properties.getMaxIdentifierSize())), - quotizedLocalColumnsSting, - quotize(fk.getForeignTable()), - quotizedForeignColumnsString); + for (final String alterTable : alterTables) { Statement alterTableStmt = null; try { diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/PostgreSqlTranslator.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/PostgreSqlTranslator.java index 7424f12b..0e006a76 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/PostgreSqlTranslator.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/PostgreSqlTranslator.java @@ -18,6 +18,9 @@ import com.feedzai.commons.sql.abstraction.ddl.AlterColumn; import com.feedzai.commons.sql.abstraction.ddl.DbColumn; import com.feedzai.commons.sql.abstraction.ddl.DbColumnConstraint; +import com.feedzai.commons.sql.abstraction.ddl.DbEntity; +import com.feedzai.commons.sql.abstraction.ddl.DbFk; +import com.feedzai.commons.sql.abstraction.ddl.DbIndex; import com.feedzai.commons.sql.abstraction.ddl.DropPrimaryKey; import com.feedzai.commons.sql.abstraction.ddl.Rename; import com.feedzai.commons.sql.abstraction.dml.Cast; @@ -44,8 +47,10 @@ import java.util.stream.Collectors; import static com.feedzai.commons.sql.abstraction.engine.configuration.PdbProperties.VARCHAR_SIZE; +import static com.feedzai.commons.sql.abstraction.util.StringUtils.md5; import static com.feedzai.commons.sql.abstraction.util.StringUtils.quotize; import static java.lang.String.format; +import static org.apache.commons.lang3.StringUtils.join; /** * Provides SQL translation for PostgreSQL. @@ -415,4 +420,124 @@ public String translateFalse() { return "FALSE"; } + @Override + public String translateCreateTable(final DbEntity entity) { + + List createTable = new ArrayList<>(); + + createTable.add("CREATE TABLE"); + createTable.add(quotize(entity.getName())); + List columns = new ArrayList<>(); + for (DbColumn c : entity.getColumns()) { + List column = new ArrayList<>(); + column.add(quotize(c.getName())); + column.add(translate(c)); + + for (DbColumnConstraint cc : c.getColumnConstraints()) { + column.add(cc.translate()); + } + + if (c.isDefaultValueSet()) { + column.add("DEFAULT"); + column.add(translate(c.getDefaultValue())); + } + + columns.add(join(column, " ")); + } + createTable.add("(" + join(columns, ", ") + ")"); + + return join(createTable, " "); + } + + @Override + public String translatePrimaryKeysConstraints(final DbEntity entity) { + final List pks = new ArrayList<>(); + for (String pk : entity.getPkFields()) { + pks.add(quotize(pk)); + } + + final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); + + final List statement = new ArrayList<>(); + statement.add("ALTER TABLE"); + statement.add(quotize(entity.getName())); + statement.add("ADD CONSTRAINT"); + statement.add(quotize(pkName)); + statement.add("PRIMARY KEY"); + statement.add("(" + join(pks, ", ") + ")"); + + return join(statement, " "); + } + + @Override + public List translateForeignKey(final DbEntity entity) { + final List alterTables = new ArrayList<>(); + + for (final DbFk fk : entity.getFks()) { + final List quotizedLocalColumns = new ArrayList<>(); + for (String s : fk.getLocalColumns()) { + quotizedLocalColumns.add(quotize(s)); + } + + final List quotizedForeignColumns = new ArrayList<>(); + for (String s : fk.getForeignColumns()) { + quotizedForeignColumns.add(quotize(s)); + } + + final String table = quotize(entity.getName()); + final String quotizedLocalColumnsSting = join(quotizedLocalColumns, ", "); + final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); + + final String alterTable = + format("ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", + table, + quotize(md5("FK_" + table + quotizedLocalColumnsSting + quotizedForeignColumnsString, + properties.getMaxIdentifierSize() + )), + quotizedLocalColumnsSting, + quotize(fk.getForeignTable()), + quotizedForeignColumnsString + ); + + alterTables.add(alterTable); + } + + return alterTables; + } + + @Override + public List translateCreateIndexes(final DbEntity entity) { + final List indexes = entity.getIndexes(); + final List createIndexes = new ArrayList<>(); + + for (final DbIndex index : indexes) { + + final List createIndex = new ArrayList<>(); + createIndex.add("CREATE"); + if (index.isUnique()) { + createIndex.add("UNIQUE"); + } + createIndex.add("INDEX"); + + final List columns = new ArrayList<>(); + final List columnsForName = new ArrayList<>(); + for (final String column : index.getColumns()) { + columns.add(quotize(column)); + columnsForName.add(column); + } + + final String idxName = md5(format("%s_%s_IDX", entity.getName(), + join(columnsForName, "_")), properties.getMaxIdentifierSize()); + + createIndex.add(quotize(idxName)); + createIndex.add("ON"); + createIndex.add(quotize(entity.getName())); + createIndex.add("(" + join(columns, ", ") + ")"); + + final String statement = join(createIndex, " "); + createIndexes.add(statement); + } + + return createIndexes; + } } diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/SqlServerEngine.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/SqlServerEngine.java index a2042089..43b68c84 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/SqlServerEngine.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/SqlServerEngine.java @@ -35,7 +35,6 @@ import com.feedzai.commons.sql.abstraction.entry.EntityEntry; import java.io.StringReader; -import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -177,43 +176,7 @@ protected int entityToPreparedStatement(final DbEntity entity, final PreparedSta @Override protected void createTable(final DbEntity entity) throws DatabaseEngineException { - List createTable = new ArrayList<>(); - - createTable.add("CREATE TABLE"); - createTable.add(quotize(entity.getName())); - List columns = new ArrayList<>(); - - int numberOfAutoIncs = 0; - for (DbColumn c : entity.getColumns()) { - List column = new ArrayList<>(); - column.add(quotize(c.getName())); - - column.add(translateType(c)); - - if (c.isAutoInc()) { - column.add("IDENTITY"); - numberOfAutoIncs++; - } - - for (DbColumnConstraint cc : c.getColumnConstraints()) { - column.add(cc.translate()); - } - - if (c.isDefaultValueSet()) { - column.add("DEFAULT"); - column.add(translate(c.getDefaultValue())); - } - - columns.add(join(column, " ")); - } - - if (numberOfAutoIncs > 1) { - throw new DatabaseEngineException("In SQLServer you can only define one auto increment column"); - } - - createTable.add("(" + join(columns, ", ") + ")"); - - final String createTableStatement = join(createTable, " "); + final String createTableStatement = translator.translateCreateTable(entity); logger.trace(createTableStatement); @@ -245,75 +208,20 @@ protected void addPrimaryKey(final DbEntity entity) throws DatabaseEngineExcepti return; } - List pks = new ArrayList<>(); - for (String pk : entity.getPkFields()) { - DbColumn toAlter = null; - for (DbColumn col : entity.getColumns()) { - if (col.getName().equals(pk)) { - toAlter = col; - break; - } - } - - if (toAlter == null) { - throw new DatabaseEngineException("The column you specified for Primary Key does not exist."); - } else { - boolean isNotNull = false; - List cons = new ArrayList<>(); - cons.add(quotize(toAlter.getName())); - cons.add(translateType(toAlter)); - for (DbColumnConstraint cc : toAlter.getColumnConstraints()) { - if (cc == DbColumnConstraint.NOT_NULL) { - isNotNull = true; - } - - cons.add(cc.translate()); - } - - if (!isNotNull) { - cons.add(DbColumnConstraint.NOT_NULL.translate()); - - final String alterTable = format("ALTER TABLE %s ALTER COLUMN %s", quotize(entity.getName()), join(cons, " ")); - logger.trace(alterTable); - Statement s = null; - try { - s = conn.createStatement(); - s.executeUpdate(alterTable); - } catch (final SQLException ex) { - throw new DatabaseEngineException("Something went wrong altering the table. This shouldn't have happened", ex); - } finally { - try { - if (s != null) { - s.close(); - } - } catch (final Exception e) { - logger.trace("Error closing statement.", e); - } - } - } - } - - pks.add(quotize(pk)); - } - - final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); - - List statement = new ArrayList<>(); - statement.add("ALTER TABLE"); - statement.add(quotize(entity.getName())); - statement.add("ADD CONSTRAINT"); - statement.add(quotize(pkName)); - statement.add("PRIMARY KEY"); - statement.add("(" + join(pks, ", ") + ")"); - - final String addPrimaryKey = join(statement, " "); - - logger.trace(addPrimaryKey); + final String notNullPk = translator.translatePrimaryKeysNotNull(entity); + final String addPrimaryKey = translator.translatePrimaryKeysConstraints(entity); Statement s = null; try { + logger.trace(notNullPk); + s = conn.createStatement(); + s.executeUpdate(notNullPk); + s.close(); + + logger.trace(addPrimaryKey); s = conn.createStatement(); s.executeUpdate(addPrimaryKey); + s.close(); } catch (final SQLException ex) { if (ex.getErrorCode() == TABLE_CAN_ONLY_HAVE_ONE_PRIMARY_KEY) { logger.debug(dev, "'{}' already has a primary key", entity.getName()); @@ -334,31 +242,7 @@ protected void addPrimaryKey(final DbEntity entity) throws DatabaseEngineExcepti @Override protected void addIndexes(final DbEntity entity) throws DatabaseEngineException { - List indexes = entity.getIndexes(); - - for (DbIndex index : indexes) { - - List createIndex = new ArrayList<>(); - createIndex.add("CREATE"); - if (index.isUnique()) { - createIndex.add("UNIQUE"); - } - createIndex.add("INDEX"); - - List columns = new ArrayList<>(); - List columnsForName = new ArrayList<>(); - for (String column : index.getColumns()) { - columns.add(quotize(column)); - columnsForName.add(column); - } - final String idxName = md5(format("%s_%s_IDX", entity.getName(), join(columnsForName, "_")), properties.getMaxIdentifierSize()); - createIndex.add(quotize(idxName)); - createIndex.add("ON"); - createIndex.add(quotize(entity.getName())); - createIndex.add("(" + join(columns, ", ") + ")"); - - final String statement = join(createIndex, " "); - + for (final String statement : translator.translateCreateIndexes(entity)) { logger.trace(statement); Statement s = null; @@ -367,7 +251,8 @@ protected void addIndexes(final DbEntity entity) throws DatabaseEngineException s.executeUpdate(statement); } catch (final SQLException ex) { if (ex.getErrorCode() == INDEX_ALREADY_EXISTS) { - logger.debug(dev, "'{}' is already defined", idxName); + logger.debug(dev, "Index is already defined"); + logger.debug(dev, ex.getMessage()); handleOperation(new OperationFault(entity.getName(), OperationFault.Type.INDEX_ALREADY_EXISTS), ex); } else { throw new DatabaseEngineException("Something went wrong handling statement", ex); @@ -592,30 +477,9 @@ protected synchronized long doPersist(final PreparedStatement ps, @Override protected void addFks(DbEntity entity) throws DatabaseEngineException { - for (DbFk fk : entity.getFks()) { - final List quotizedLocalColumns = new ArrayList<>(); - for (String s : fk.getLocalColumns()) { - quotizedLocalColumns.add(quotize(s)); - } - - final List quotizedForeignColumns = new ArrayList<>(); - for (String s : fk.getForeignColumns()) { - quotizedForeignColumns.add(quotize(s)); - } - - final String table = quotize(entity.getName()); - final String quotizedLocalColumnsSting = join(quotizedLocalColumns, ", "); - final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); - - final String alterTable = - format( - "ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", - table, - quotize(md5("FK_" + table + quotizedLocalColumnsSting + quotizedForeignColumnsString, properties.getMaxIdentifierSize())), - quotizedLocalColumnsSting, - quotize(fk.getForeignTable()), - quotizedForeignColumnsString); + final List alterTables = translator.translateForeignKey(entity); + for (final String alterTable : alterTables) { Statement alterTableStmt = null; try { diff --git a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/SqlServerTranslator.java b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/SqlServerTranslator.java index 612873ff..92f5580c 100644 --- a/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/SqlServerTranslator.java +++ b/src/main/java/com/feedzai/commons/sql/abstraction/engine/impl/SqlServerTranslator.java @@ -18,6 +18,9 @@ import com.feedzai.commons.sql.abstraction.ddl.AlterColumn; import com.feedzai.commons.sql.abstraction.ddl.DbColumn; import com.feedzai.commons.sql.abstraction.ddl.DbColumnConstraint; +import com.feedzai.commons.sql.abstraction.ddl.DbEntity; +import com.feedzai.commons.sql.abstraction.ddl.DbFk; +import com.feedzai.commons.sql.abstraction.ddl.DbIndex; import com.feedzai.commons.sql.abstraction.ddl.DropPrimaryKey; import com.feedzai.commons.sql.abstraction.ddl.Rename; import com.feedzai.commons.sql.abstraction.dml.Cast; @@ -32,20 +35,26 @@ import com.feedzai.commons.sql.abstraction.dml.Update; import com.feedzai.commons.sql.abstraction.dml.View; import com.feedzai.commons.sql.abstraction.engine.AbstractTranslator; +import com.feedzai.commons.sql.abstraction.engine.DatabaseEngineException; import com.feedzai.commons.sql.abstraction.engine.DatabaseEngineRuntimeException; import com.feedzai.commons.sql.abstraction.engine.OperationNotSupportedRuntimeException; import com.feedzai.commons.sql.abstraction.util.StringUtils; import com.google.common.base.Joiner; import com.google.common.collect.Lists; +import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.List; +import static com.feedzai.commons.sql.abstraction.dml.dialect.SqlBuilder.all; import static com.feedzai.commons.sql.abstraction.dml.dialect.SqlBuilder.column; import static com.feedzai.commons.sql.abstraction.engine.configuration.PdbProperties.MAX_BLOB_SIZE; import static com.feedzai.commons.sql.abstraction.engine.configuration.PdbProperties.VARCHAR_SIZE; +import static com.feedzai.commons.sql.abstraction.util.StringUtils.md5; import static com.feedzai.commons.sql.abstraction.util.StringUtils.quotize; import static java.lang.String.format; +import static org.apache.commons.lang3.StringUtils.join; /** * Provides SQL translation for SQLServer. @@ -455,6 +464,179 @@ public String translateFalse() { return "0"; } + @Override + public String translateCreateTable(final DbEntity entity) { + final List createTable = new ArrayList<>(); + + createTable.add("CREATE TABLE"); + createTable.add(quotize(entity.getName())); + final List columns = new ArrayList<>(); + + int numberOfAutoIncs = 0; + for (DbColumn c : entity.getColumns()) { + final List column = new ArrayList<>(); + column.add(quotize(c.getName())); + + column.add(translate(c)); + + if (c.isAutoInc()) { + column.add("IDENTITY"); + numberOfAutoIncs++; + } + + for (DbColumnConstraint cc : c.getColumnConstraints()) { + column.add(cc.translate()); + } + + if (c.isDefaultValueSet()) { + column.add("DEFAULT"); + column.add(translate(c.getDefaultValue())); + } + + columns.add(join(column, " ")); + } + + if (numberOfAutoIncs > 1) { + throw new DatabaseEngineRuntimeException("In SQLServer you can only define one auto increment column"); + } + + createTable.add("(" + join(columns, ", ") + ")"); + + return join(createTable, " "); + } + + @Override + public String translatePrimaryKeysNotNull(final DbEntity entity) { + final List notNull = new ArrayList<>(); + for (final String pk : entity.getPkFields()) { + DbColumn toAlter = null; + for (DbColumn col : entity.getColumns()) { + if (col.getName().equals(pk)) { + toAlter = col; + break; + } + } + + if (toAlter == null) { + throw new DatabaseEngineRuntimeException("The column you specified for Primary Key does not exist."); + } else { + boolean isNotNull = false; + final List cons = new ArrayList<>(); + cons.add(quotize(toAlter.getName())); + cons.add(translate(toAlter)); + for (DbColumnConstraint cc : toAlter.getColumnConstraints()) { + if (cc == DbColumnConstraint.NOT_NULL) { + isNotNull = true; + } + + cons.add(cc.translate()); + } + + if (!isNotNull) { + cons.add(DbColumnConstraint.NOT_NULL.translate()); + + final String alterTable = format("ALTER TABLE %s ALTER COLUMN %s", + quotize(entity.getName()), join(cons, " ")); + notNull.add(alterTable); + } + } + } + + return join(notNull, " ; "); + } + + @Override + public String translatePrimaryKeysConstraints(final DbEntity entity) { + final List pks = new ArrayList<>(); + for (final String pk : entity.getPkFields()) { + pks.add(quotize(pk)); + } + + final String pkName = md5(format("PK_%s", entity.getName()), properties.getMaxIdentifierSize()); + + final List statement = new ArrayList<>(); + statement.add("ALTER TABLE"); + statement.add(quotize(entity.getName())); + statement.add("ADD CONSTRAINT"); + statement.add(quotize(pkName)); + statement.add("PRIMARY KEY"); + statement.add("(" + join(pks, ", ") + ")"); + + return join(statement, " "); + } + + @Override + public List translateForeignKey(final DbEntity entity) { + final List alterTables = new ArrayList<>(); + + for (final DbFk fk : entity.getFks()) { + final List quotizedLocalColumns = new ArrayList<>(); + for (String s : fk.getLocalColumns()) { + quotizedLocalColumns.add(quotize(s)); + } + + final List quotizedForeignColumns = new ArrayList<>(); + for (final String s : fk.getForeignColumns()) { + quotizedForeignColumns.add(quotize(s)); + } + + final String table = quotize(entity.getName()); + final String quotizedLocalColumnsSting = join(quotizedLocalColumns, ", "); + final String quotizedForeignColumnsString = join(quotizedForeignColumns, ", "); + + final String alterTable = + format("ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)", + table, + quotize(md5("FK_" + table + quotizedLocalColumnsSting + quotizedForeignColumnsString, + properties.getMaxIdentifierSize() + )), + quotizedLocalColumnsSting, + quotize(fk.getForeignTable()), + quotizedForeignColumnsString + ); + + alterTables.add(alterTable); + } + + return alterTables; + } + + @Override + public List translateCreateIndexes(final DbEntity entity) { + final List createIndexes = new ArrayList<>(); + final List indexes = entity.getIndexes(); + + for (final DbIndex index : indexes) { + + final List createIndex = new ArrayList<>(); + createIndex.add("CREATE"); + if (index.isUnique()) { + createIndex.add("UNIQUE"); + } + createIndex.add("INDEX"); + + final List columns = new ArrayList<>(); + final List columnsForName = new ArrayList<>(); + for (final String column : index.getColumns()) { + columns.add(quotize(column)); + columnsForName.add(column); + } + + final String idxName = md5(format("%s_%s_IDX", entity.getName(), + join(columnsForName, "_")), properties.getMaxIdentifierSize()); + + createIndex.add(quotize(idxName)); + createIndex.add("ON"); + createIndex.add(quotize(entity.getName())); + createIndex.add("(" + join(columns, ", ") + ")"); + + final String statement = join(createIndex, " "); + + createIndexes.add(statement); + } + return createIndexes; + } + /** * Helper method which removes the environment parameter from a column when it is used inside an order by statement * and in a paginated query. This is needed in order to avoid "The multi-part identifier could not be bound" error diff --git a/src/test/java/com/feedzai/commons/sql/abstraction/engine/testconfig/CustomTranslator.java b/src/test/java/com/feedzai/commons/sql/abstraction/engine/testconfig/CustomTranslator.java index 880e1eb0..c10eb987 100644 --- a/src/test/java/com/feedzai/commons/sql/abstraction/engine/testconfig/CustomTranslator.java +++ b/src/test/java/com/feedzai/commons/sql/abstraction/engine/testconfig/CustomTranslator.java @@ -15,8 +15,11 @@ */ package com.feedzai.commons.sql.abstraction.engine.testconfig; +import java.util.List; + import com.feedzai.commons.sql.abstraction.ddl.AlterColumn; import com.feedzai.commons.sql.abstraction.ddl.DbColumn; +import com.feedzai.commons.sql.abstraction.ddl.DbEntity; import com.feedzai.commons.sql.abstraction.ddl.DropPrimaryKey; import com.feedzai.commons.sql.abstraction.ddl.Rename; import com.feedzai.commons.sql.abstraction.dml.*; @@ -97,4 +100,29 @@ public String translate(final Cast cast) { public String translate(final StringAgg stringAgg) { return null; } + + @Override + public String translateCreateTable(final DbEntity entity) { + return null; + } + + @Override + public String translatePrimaryKeysConstraints(final DbEntity entity) { + return null; + } + + @Override + public List translateForeignKey(final DbEntity entity) { + return null; + } + + @Override + public List translateCreateIndexes(final DbEntity entity) { + return null; + } + + @Override + public List translateCreateSequences(final DbEntity entity) { + return null; + } }