diff --git a/src/main/java/com/github/mjeanroy/dbunit/commons/lang/PreConditions.java b/src/main/java/com/github/mjeanroy/dbunit/commons/lang/PreConditions.java index 07432a1a..e6b25ec6 100644 --- a/src/main/java/com/github/mjeanroy/dbunit/commons/lang/PreConditions.java +++ b/src/main/java/com/github/mjeanroy/dbunit/commons/lang/PreConditions.java @@ -25,6 +25,7 @@ package com.github.mjeanroy.dbunit.commons.lang; import static com.github.mjeanroy.dbunit.commons.lang.Strings.isBlank; +import static com.github.mjeanroy.dbunit.commons.lang.Strings.isEmpty; /** * Static PreConditions Utilities. @@ -52,6 +53,26 @@ public static T notNull(T value, String message, Object... params) { return value; } + /** + * Ensure that given {@code value} is not {@code null}, empty. + * + * @param value Value to check. + * @param message Error message. + * @param params Optional error message parameters. + * @return Value if it is not {@code null}, empty. + * @throws NullPointerException If {@code value} is null. + * @throws IllegalArgumentException If {@code value} is empty. + */ + public static String notEmpty(String value, String message, Object... params) { + notNull(value, message, params); + + if (isEmpty(value)) { + throw new IllegalArgumentException(format(message, params)); + } + + return value; + } + /** * Ensure that given {@code value} is not {@code null}, empty or blank. * diff --git a/src/main/java/com/github/mjeanroy/dbunit/commons/lang/ToStringBuilder.java b/src/main/java/com/github/mjeanroy/dbunit/commons/lang/ToStringBuilder.java index b7b5745f..5d7efdbc 100644 --- a/src/main/java/com/github/mjeanroy/dbunit/commons/lang/ToStringBuilder.java +++ b/src/main/java/com/github/mjeanroy/dbunit/commons/lang/ToStringBuilder.java @@ -80,6 +80,17 @@ public static ToStringBuilder create(Class klass) { return new ToStringBuilder(klass); } + /** + * Create the builder with given class of instance (the simple name will be used to start + * the {@code toString} value). + * + * @param instance The instance. + * @return The builder. + */ + public static ToStringBuilder create(Object instance) { + return new ToStringBuilder(instance.getClass()); + } + /** * The internal string builder. */ diff --git a/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/AbstractJdbcDropCreateForeignKeyManager.java b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/AbstractJdbcDropCreateForeignKeyManager.java new file mode 100644 index 00000000..b5d82c31 --- /dev/null +++ b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/AbstractJdbcDropCreateForeignKeyManager.java @@ -0,0 +1,128 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.exception.JdbcException; +import com.github.mjeanroy.dbunit.loggers.Logger; +import com.github.mjeanroy.dbunit.loggers.Loggers; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; + +import static com.github.mjeanroy.dbunit.core.jdbc.JdbcUtils.executeUpdates; + +abstract class AbstractJdbcDropCreateForeignKeyManager implements JdbcForeignKeyManager { + + private static final Logger log = Loggers.getLogger(AbstractJdbcDropCreateForeignKeyManager.class); + + private List foreignKeys; + + AbstractJdbcDropCreateForeignKeyManager() { + // Initial state. + this.foreignKeys = null; + } + + @Override + public final synchronized void disable(Connection connection) { + checkInitialState(); + + // Introspect foreign keys, so we can drop them + // and re-create them in the exact same configuration later. + foreignKeys = introspectForeignKeys(connection); + + // We can now drop foreign these foreign keys. + dropForeignKeys(connection); + } + + @Override + public final synchronized void enable(Connection connection) { + // Re-create foreign keys with the same initial configuration. + reCreateForeignKeys(connection); + + // Go back to initial state. + foreignKeys = null; + } + + private void checkInitialState() { + if (foreignKeys != null) { + throw new IllegalStateException( + "Cannot disable constraints, foreign keys have been dropped, please re-enable them before" + ); + } + } + + private void dropForeignKeys(Connection connection) { + if (foreignKeys == null) { + throw new IllegalStateException( + "Cannot disable constraints, foreign keys have not been introspected" + ); + } + + List queries = new ArrayList<>(foreignKeys.size()); + for (T foreignKey : foreignKeys) { + queries.add( + generateDropForeignKeyQuery(foreignKey) + ); + } + + try { + executeUpdates(connection, queries); + } + catch (Exception ex) { + log.error(ex.getMessage(), ex); + throw new JdbcException("Cannot disable foreign key constraints", ex); + } + } + + private void reCreateForeignKeys(Connection connection) { + if (foreignKeys == null) { + throw new IllegalStateException( + "Cannot enable constraints, foreign keys have not been introspected, try disabling constraints first" + ); + } + + List queries = new ArrayList<>(foreignKeys.size()); + for (T foreignKey : foreignKeys) { + queries.add( + generateAddForeignKeyQuery(foreignKey) + ); + } + + try { + executeUpdates(connection, queries); + } + catch (Exception ex) { + log.error(ex.getMessage(), ex); + throw new JdbcException("Cannot enable foreign key constraints, please check your dataset", ex); + } + } + + abstract List introspectForeignKeys(Connection connection); + + abstract String generateDropForeignKeyQuery(T foreignKey); + + abstract String generateAddForeignKeyQuery(T foreignKey); +} diff --git a/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/HsqldbForeignKeyManager.java b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/HsqldbForeignKeyManager.java new file mode 100644 index 00000000..059e6370 --- /dev/null +++ b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/HsqldbForeignKeyManager.java @@ -0,0 +1,272 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.commons.lang.ToStringBuilder; +import com.github.mjeanroy.dbunit.core.jdbc.JdbcUtils.ResultSetMapFunction; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.util.List; +import java.util.Objects; + +import static com.github.mjeanroy.dbunit.commons.lang.PreConditions.notEmpty; +import static com.github.mjeanroy.dbunit.core.jdbc.JdbcUtils.executeQuery; + +final class HsqldbForeignKeyManager extends AbstractJdbcDropCreateForeignKeyManager { + + private static final String C_CONSTRAINT_NAME = "constraint_name"; + private static final String C_TABLE_SCHEMA = "table_schema"; + private static final String C_TABLE_NAME = "table_name"; + private static final String C_TABLE_COLUMNS = "table_columns"; + private static final String C_REFERENCED_TABLE_SCHEMA = "referenced_table_schema"; + private static final String C_REFERENCED_TABLE_NAME = "referenced_table_name"; + private static final String C_REFERENCED_TABLE_COLUMNS = "referenced_columns"; + private static final String C_UPDATE_RULE = "update_rule"; + private static final String C_DELETE_RULE = "delete_rule"; + private static final String C_DEFERRABLE = "deferrable"; + private static final String C_INITIALLY_DEFERRED = "initially_deferred"; + + private static final ForeignKeyConstraintsMapFunction mapFunction = new ForeignKeyConstraintsMapFunction(); + + HsqldbForeignKeyManager() { + } + + @Override + List introspectForeignKeys(Connection connection) { + String query = String.format( + "SELECT" + + " SC.FK_NAME AS %s," + + " SC.FKTABLE_SCHEM AS %s," + + " SC.FKTABLE_NAME AS %s," + + " GROUP_CONCAT(SC.FKCOLUMN_NAME SEPARATOR ',') AS %s," + + " SC.PKTABLE_SCHEM AS %s," + + " SC.PKTABLE_NAME AS %s," + + " GROUP_CONCAT(SC.PKCOLUMN_NAME SEPARATOR ',') AS %s," + + " CASE" + + " WHEN SC.UPDATE_RULE = 0 THEN 'CASCADE'" + + " WHEN SC.UPDATE_RULE = 1 THEN 'NO ACTION'" + + " WHEN SC.UPDATE_RULE = 2 THEN 'SET NULL'" + + " WHEN SC.UPDATE_RULE = 3 THEN 'RESTRICT'" + + " ELSE 'SET DEFAULT'" + + " END AS %s," + + " CASE" + + " WHEN SC.DELETE_RULE = 0 THEN 'CASCADE'" + + " WHEN SC.DELETE_RULE = 1 THEN 'NO ACTION'" + + " WHEN SC.DELETE_RULE = 2 THEN 'SET NULL'" + + " WHEN SC.DELETE_RULE = 3 THEN 'RESTRICT'" + + " ELSE 'SET DEFAULT'" + + " END AS %s," + + " CASE" + + " WHEN TC.IS_DEFERRABLE = 'NO' THEN 'NOT DEFERRABLE'" + + " ELSE 'DEFERRABLE'" + + " END AS %s," + + " CASE" + + " WHEN TC.INITIALLY_DEFERRED = 'NO' THEN 'INITIALLY IMMEDIATE'" + + " ELSE 'INITIALLY DEFERRED'" + + " END AS %s " + + "FROM INFORMATION_SCHEMA.SYSTEM_CROSSREFERENCE SC " + + "LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC " + + " ON TC.CONSTRAINT_SCHEMA = SC.FKTABLE_SCHEM" + + " AND TC.CONSTRAINT_NAME = SC.FK_NAME" + + " AND TC.CONSTRAINT_CATALOG = SC.FKTABLE_CAT " + + "GROUP BY" + + " SC.FK_NAME," + + " SC.FKTABLE_SCHEM," + + " SC.FKTABLE_NAME," + + " SC.PKTABLE_SCHEM," + + " SC.PKTABLE_NAME," + + " SC.UPDATE_RULE," + + " SC.DELETE_RULE," + + " TC.IS_DEFERRABLE," + + " TC.INITIALLY_DEFERRED;", + + C_CONSTRAINT_NAME, + C_TABLE_SCHEMA, + C_TABLE_NAME, + C_TABLE_COLUMNS, + C_REFERENCED_TABLE_SCHEMA, + C_REFERENCED_TABLE_NAME, + C_REFERENCED_TABLE_COLUMNS, + C_UPDATE_RULE, + C_DELETE_RULE, + C_DEFERRABLE, + C_INITIALLY_DEFERRED + ); + + return executeQuery( + connection, + query, + mapFunction + ); + } + + @Override + String generateDropForeignKeyQuery(ForeignKeyConstraint fk) { + return String.format( + "ALTER TABLE %s.%s DROP FOREIGN KEY %s;", + fk.tableSchema, + fk.tableName, + fk.constraintName + ); + } + + @Override + String generateAddForeignKeyQuery(ForeignKeyConstraint fk) { + return String.format( + "ALTER TABLE %s.%s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s.%s (%s) ON UPDATE %s ON DELETE %s;", + fk.tableSchema, + fk.tableName, + fk.constraintName, + fk.columnName, + fk.referencedTableSchema, + fk.referencedTableName, + fk.referencedColumnName, + fk.updateRule, + fk.deleteRule + ); + } + + static final class ForeignKeyConstraint { + private final String constraintName; + private final String tableSchema; + private final String tableName; + private final String columnName; + private final String referencedTableSchema; + private final String referencedTableName; + private final String referencedColumnName; + private final String updateRule; + private final String deleteRule; + private final String deferrable; + private final String initiallyDeferred; + + private ForeignKeyConstraint( + String constraintName, + String tableSchema, + String tableName, + String columnName, + String referencedTableSchema, + String referencedTableName, + String referencedColumnName, + String updateRule, + String deleteRule, + String deferrable, + String initiallyDeferred + ) { + this.constraintName = notEmpty(constraintName, "constraintName must be defined"); + this.tableSchema = notEmpty(tableSchema, "tableSchema must be defined"); + this.tableName = notEmpty(tableName, "tableName must be defined"); + this.columnName = notEmpty(columnName, "columnName must be defined"); + this.referencedTableSchema = notEmpty(referencedTableSchema, "referencedTableSchema must be defined"); + this.referencedTableName = notEmpty(referencedTableName, "referencedTableName must be defined"); + this.referencedColumnName = notEmpty(referencedColumnName, "referencedColumnName must be defined"); + this.updateRule = notEmpty(updateRule, "updateRule must be defined"); + this.deleteRule = notEmpty(deleteRule, "deleteRule must be defined"); + this.deferrable = notEmpty(deferrable, "deferrable must be defined"); + this.initiallyDeferred = notEmpty(initiallyDeferred, "initiallyDeferred must be defined"); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (o instanceof ForeignKeyConstraint) { + ForeignKeyConstraint fk = (ForeignKeyConstraint) o; + return Objects.equals(constraintName, fk.constraintName) + && Objects.equals(tableSchema, fk.tableSchema) + && Objects.equals(tableName, fk.tableName) + && Objects.equals(columnName, fk.columnName) + && Objects.equals(referencedTableSchema, fk.referencedTableSchema) + && Objects.equals(referencedTableName, fk.referencedTableName) + && Objects.equals(referencedColumnName, fk.referencedColumnName) + && Objects.equals(updateRule, fk.updateRule) + && Objects.equals(deleteRule, fk.deleteRule) + && Objects.equals(deferrable, fk.deferrable) + && Objects.equals(initiallyDeferred, fk.initiallyDeferred); + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash( + constraintName, + tableSchema, + tableName, + columnName, + referencedTableSchema, + referencedTableName, + referencedColumnName, + updateRule, + deleteRule, + deferrable, + initiallyDeferred + ); + } + + @Override + public String toString() { + return ToStringBuilder.create(this) + .append("constraintName", constraintName) + .append("tableSchema", tableSchema) + .append("tableName", tableName) + .append("columnName", columnName) + .append("referencedTableSchema", referencedTableSchema) + .append("referencedTableName", referencedTableName) + .append("referencedColumnName", referencedColumnName) + .append("updateRule", updateRule) + .append("deleteRule", deleteRule) + .append("deferrable", deferrable) + .append("initiallyDeferred", initiallyDeferred) + .build(); + } + } + + private static final class ForeignKeyConstraintsMapFunction implements ResultSetMapFunction { + + private ForeignKeyConstraintsMapFunction() { + } + + @Override + public ForeignKeyConstraint apply(ResultSet resultSet) throws Exception { + return new ForeignKeyConstraint( + resultSet.getString(C_CONSTRAINT_NAME), + resultSet.getString(C_TABLE_SCHEMA), + resultSet.getString(C_TABLE_NAME), + resultSet.getString(C_TABLE_COLUMNS), + resultSet.getString(C_REFERENCED_TABLE_SCHEMA), + resultSet.getString(C_REFERENCED_TABLE_NAME), + resultSet.getString(C_REFERENCED_TABLE_COLUMNS), + resultSet.getString(C_UPDATE_RULE), + resultSet.getString(C_DELETE_RULE), + resultSet.getString(C_DEFERRABLE), + resultSet.getString(C_INITIALLY_DEFERRED) + ); + } + } +} diff --git a/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/JdbcForeignKeyManager.java b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/JdbcForeignKeyManager.java new file mode 100644 index 00000000..5e294dfe --- /dev/null +++ b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/JdbcForeignKeyManager.java @@ -0,0 +1,34 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import java.sql.Connection; + +public interface JdbcForeignKeyManager { + + void disable(Connection connection) throws Exception; + + void enable(Connection connection) throws Exception; +} diff --git a/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/JdbcUtils.java b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/JdbcUtils.java index a7377436..c4d4ca94 100644 --- a/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/JdbcUtils.java +++ b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/JdbcUtils.java @@ -25,10 +25,26 @@ package com.github.mjeanroy.dbunit.core.jdbc; import com.github.mjeanroy.dbunit.exception.JdbcException; +import com.github.mjeanroy.dbunit.loggers.Logger; +import com.github.mjeanroy.dbunit.loggers.Loggers; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; final class JdbcUtils { + private static final Logger log = Loggers.getLogger(JdbcUtils.class); + + private JdbcUtils() { + } + static void loadDriver(String driverClassName) { + log.info("Loading driver: {}", driverClassName); + try { Class.forName(driverClassName); } @@ -36,4 +52,51 @@ static void loadDriver(String driverClassName) { throw new JdbcException("Cannot load JDBC Driver: " + driverClassName, ex); } } + + static ResultSet executeQuery(Connection connection, String query) { + log.debug("Executing query: {}", query); + + try { + return connection.createStatement().executeQuery(query); + } + catch (Exception ex) { + throw new JdbcException("Cannot execute query: " + query, ex); + } + } + + static List executeQuery(Connection connection, String query, ResultSetMapFunction mapFunction) { + log.debug("Executing query: {}", query); + + try (ResultSet resultSet = connection.createStatement().executeQuery(query)) { + log.debug("Extracting query results"); + List outputs = new ArrayList<>(); + while (resultSet.next()) { + outputs.add(mapFunction.apply(resultSet)); + } + + return outputs; + } + catch (Exception ex) { + throw new JdbcException("Cannot execute query: " + query, ex); + } + } + + static void executeUpdates(Connection connection, Collection queries) { + log.debug("Extracting batch queries: {}", queries); + try (Statement statement = connection.createStatement()) { + for (String query : queries) { + log.debug("Adding query `{}` to batch statement", query); + statement.addBatch(query); + } + + statement.executeBatch(); + } + catch (Exception ex) { + throw new JdbcException("Cannot execute queries: " + queries, ex); + } + } + + interface ResultSetMapFunction { + T apply(ResultSet resultSet) throws Exception; + } } diff --git a/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MariaDBForeignKeyManager.java b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MariaDBForeignKeyManager.java new file mode 100644 index 00000000..382eb0e1 --- /dev/null +++ b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MariaDBForeignKeyManager.java @@ -0,0 +1,48 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import java.sql.Connection; + +final class MariaDBForeignKeyManager implements JdbcForeignKeyManager { + + // MariaDB is "just" a fork of MySQL, so it's a "MySQL" like engine. + // Let's reuse the MySQL implementation. + private final MySQLForeignKeyManager mySQLForeignKeyManager; + + MariaDBForeignKeyManager() { + mySQLForeignKeyManager = new MySQLForeignKeyManager(); + } + + @Override + public void disable(Connection connection) { + mySQLForeignKeyManager.disable(connection); + } + + @Override + public void enable(Connection connection) { + mySQLForeignKeyManager.enable(connection); + } +} diff --git a/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MsSQLForeignKeyManager.java b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MsSQLForeignKeyManager.java new file mode 100644 index 00000000..7efc8468 --- /dev/null +++ b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MsSQLForeignKeyManager.java @@ -0,0 +1,159 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.commons.lang.ToStringBuilder; +import com.github.mjeanroy.dbunit.core.jdbc.JdbcUtils.ResultSetMapFunction; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.util.List; +import java.util.Objects; + +import static com.github.mjeanroy.dbunit.commons.lang.PreConditions.notEmpty; +import static com.github.mjeanroy.dbunit.core.jdbc.JdbcUtils.executeQuery; + +final class MsSQLForeignKeyManager extends AbstractJdbcDropCreateForeignKeyManager { + + private static final String C_CONSTRAINT_NAME = "constraint_name"; + private static final String C_TABLE_SCHEMA = "table_schema"; + private static final String C_TABLE_NAME = "table_name"; + + private static final ForeignKeyConstraintsMapFunction mapFunction = new ForeignKeyConstraintsMapFunction(); + + MsSQLForeignKeyManager() { + } + + @Override + List introspectForeignKeys(Connection connection) { + String query = String.format( + "SELECT" + + " QUOTENAME(fk.name) AS %s," + + " QUOTENAME(cs.name) AS %s," + + " QUOTENAME(ct.name) AS %s " + + "FROM sys.foreign_keys AS fk " + + "INNER JOIN sys.tables AS ct ON fk.parent_object_id = ct.object_id " + + "INNER JOIN sys.schemas AS cs ON ct.schema_id = cs.schema_id " + + "WHERE fk.is_disabled = 0;", + + C_CONSTRAINT_NAME, + C_TABLE_SCHEMA, + C_TABLE_NAME + ); + + return executeQuery( + connection, + query, + mapFunction + ); + } + + @Override + String generateDropForeignKeyQuery(ForeignKeyConstraint fk) { + return String.format( + "ALTER TABLE %s.%s NOCHECK CONSTRAINT %s;", + fk.tableSchema, + fk.tableName, + fk.constraintName + ); + } + + @Override + String generateAddForeignKeyQuery(ForeignKeyConstraint fk) { + return String.format( + "ALTER TABLE %s.%s " + + "WITH CHECK " + + "CHECK CONSTRAINT %s;", + fk.tableSchema, + fk.tableName, + fk.constraintName + ); + } + + static final class ForeignKeyConstraint { + private final String constraintName; + private final String tableSchema; + private final String tableName; + + private ForeignKeyConstraint( + String constraintName, + String tableSchema, + String tableName + ) { + this.constraintName = notEmpty(constraintName, "constraintName must be defined"); + this.tableSchema = notEmpty(tableSchema, "tableSchema must be defined"); + this.tableName = notEmpty(tableName, "tableName must be defined"); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (o instanceof ForeignKeyConstraint) { + ForeignKeyConstraint fk = (ForeignKeyConstraint) o; + return Objects.equals(constraintName, fk.constraintName) + && Objects.equals(tableSchema, fk.tableSchema) + && Objects.equals(tableName, fk.tableName); + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash( + constraintName, + tableSchema, + tableName + ); + } + + @Override + public String toString() { + return ToStringBuilder.create(this) + .append("constraintName", constraintName) + .append("tableSchema", tableSchema) + .append("tableName", tableName) + .build(); + } + } + + private static final class ForeignKeyConstraintsMapFunction implements ResultSetMapFunction { + + private ForeignKeyConstraintsMapFunction() { + } + + @Override + public ForeignKeyConstraint apply(ResultSet resultSet) throws Exception { + return new ForeignKeyConstraint( + resultSet.getString(C_CONSTRAINT_NAME), + resultSet.getString(C_TABLE_SCHEMA), + resultSet.getString(C_TABLE_NAME) + ); + } + } +} diff --git a/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MySQLForeignKeyManager.java b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MySQLForeignKeyManager.java new file mode 100644 index 00000000..68f2bc77 --- /dev/null +++ b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MySQLForeignKeyManager.java @@ -0,0 +1,255 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.commons.lang.ToStringBuilder; +import com.github.mjeanroy.dbunit.core.jdbc.JdbcUtils.ResultSetMapFunction; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.util.List; +import java.util.Objects; + +import static com.github.mjeanroy.dbunit.commons.lang.PreConditions.notEmpty; +import static com.github.mjeanroy.dbunit.core.jdbc.JdbcUtils.executeQuery; + +final class MySQLForeignKeyManager extends AbstractJdbcDropCreateForeignKeyManager { + + private static final String C_CONSTRAINT_SCHEMA = "constraint_schema"; + private static final String C_CONSTRAINT_NAME = "constraint_name"; + private static final String C_TABLE_SCHEMA = "table_schema"; + private static final String C_TABLE_NAME = "table_name"; + private static final String C_REFERENCED_TABLE_SCHEMA = "referenced_table_schema"; + private static final String C_REFERENCED_TABLE_NAME = "referenced_table_name"; + private static final String C_UPDATE_RULE = "update_rule"; + private static final String C_DELETE_RULE = "delete_rule"; + private static final String C_TABLE_COLUMNS = "table_columns"; + private static final String C_REFERENCED_TABLE_COLUMNS = "referenced_table_columns"; + + private static final ForeignKeyConstraintsMapFunction mapFunction = new ForeignKeyConstraintsMapFunction(); + + MySQLForeignKeyManager() { + } + + @Override + List introspectForeignKeys(Connection connection) { + String query = String.format( + "SELECT" + + " KCU.CONSTRAINT_SCHEMA AS %s," + + " KCU.CONSTRAINT_NAME AS %s," + + " KCU.TABLE_SCHEMA AS %s," + + " KCU.TABLE_NAME AS %s," + + " KCU.REFERENCED_TABLE_SCHEMA AS %s," + + " KCU.REFERENCED_TABLE_NAME AS %s," + + " RC.UPDATE_RULE AS %s," + + " RC.DELETE_RULE AS %s," + + " GROUP_CONCAT(KCU.COLUMN_NAME) AS %s," + + " GROUP_CONCAT(KCU.REFERENCED_COLUMN_NAME) AS %s " + + "FROM information_schema.KEY_COLUMN_USAGE KCU " + + "INNER JOIN information_schema.REFERENTIAL_CONSTRAINTS RC ON RC.CONSTRAINT_NAME = KCU.CONSTRAINT_NAME AND RC.CONSTRAINT_SCHEMA = KCU.CONSTRAINT_SCHEMA " + + "GROUP BY" + + " %s," + + " %s," + + " %s," + + " %s," + + " %s," + + " %s," + + " %s," + + " %s " + + "ORDER BY %s;", + + // Select + C_CONSTRAINT_SCHEMA, + C_CONSTRAINT_NAME, + C_TABLE_SCHEMA, + C_TABLE_NAME, + C_REFERENCED_TABLE_SCHEMA, + C_REFERENCED_TABLE_NAME, + C_UPDATE_RULE, + C_DELETE_RULE, + C_TABLE_COLUMNS, + C_REFERENCED_TABLE_COLUMNS, + + // Group by + C_CONSTRAINT_SCHEMA, + C_CONSTRAINT_NAME, + C_TABLE_SCHEMA, + C_TABLE_NAME, + C_REFERENCED_TABLE_SCHEMA, + C_REFERENCED_TABLE_NAME, + C_UPDATE_RULE, + C_DELETE_RULE, + + // Order by + C_CONSTRAINT_NAME + ); + + return executeQuery( + connection, + query, + mapFunction + ); + } + + @Override + String generateDropForeignKeyQuery(ForeignKeyConstraint fk) { + return String.format( + "ALTER TABLE %s.%s DROP FOREIGN KEY %s;", + fk.tableSchema, + fk.tableName, + fk.constraintName + ); + } + + @Override + String generateAddForeignKeyQuery(ForeignKeyConstraint fk) { + return String.format( + "ALTER TABLE %s.%s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s.%s (%s) ON UPDATE %s ON DELETE %s;", + fk.tableSchema, + fk.tableName, + fk.constraintName, + fk.columnName, + fk.referencedTableSchema, + fk.referencedTableName, + fk.referencedColumnName, + fk.updateRule, + fk.deleteRule + ); + } + + static final class ForeignKeyConstraint { + private final String constraintSchema; + private final String constraintName; + private final String tableSchema; + private final String tableName; + private final String columnName; + private final String referencedTableSchema; + private final String referencedTableName; + private final String referencedColumnName; + private final String updateRule; + private final String deleteRule; + + private ForeignKeyConstraint( + String constraintSchema, + String constraintName, + String tableSchema, + String tableName, + String columnName, + String referencedTableSchema, + String referencedTableName, + String referencedColumnName, + String updateRule, + String deleteRule + ) { + this.constraintSchema = notEmpty(constraintSchema, "constraintSchema must be defined"); + this.constraintName = notEmpty(constraintName, "constraintName must be defined"); + this.tableSchema = notEmpty(tableSchema, "tableSchema must be defined"); + this.tableName = notEmpty(tableName, "tableName must be defined"); + this.columnName = notEmpty(columnName, "columnName must be defined"); + this.referencedTableSchema = notEmpty(referencedTableSchema, "referencedTableSchema must be defined"); + this.referencedTableName = notEmpty(referencedTableName, "referencedTableName must be defined"); + this.referencedColumnName = notEmpty(referencedColumnName, "referencedColumnName must be defined"); + this.updateRule = notEmpty(updateRule, "updateRule must be defined"); + this.deleteRule = notEmpty(deleteRule, "deleteRule must be defined"); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (o instanceof ForeignKeyConstraint) { + ForeignKeyConstraint fk = (ForeignKeyConstraint) o; + return Objects.equals(constraintSchema, fk.constraintSchema) + && Objects.equals(constraintName, fk.constraintName) + && Objects.equals(tableSchema, fk.tableSchema) + && Objects.equals(tableName, fk.tableName) + && Objects.equals(columnName, fk.columnName) + && Objects.equals(referencedTableSchema, fk.referencedTableSchema) + && Objects.equals(referencedTableName, fk.referencedTableName) + && Objects.equals(referencedColumnName, fk.referencedColumnName) + && Objects.equals(updateRule, fk.updateRule) + && Objects.equals(deleteRule, fk.deleteRule); + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash( + constraintSchema, + constraintName, + tableSchema, + tableName, + columnName, + referencedTableSchema, + referencedTableName, + referencedColumnName, + updateRule, + deleteRule + ); + } + + @Override + public String toString() { + return ToStringBuilder.create(this) + .append("constraintSchema", constraintSchema) + .append("constraintName", constraintName) + .append("tableSchema", tableSchema) + .append("tableName", tableName) + .append("columnName", columnName) + .append("referencedTableSchema", referencedTableSchema) + .append("referencedTableName", referencedTableName) + .append("referencedColumnName", referencedColumnName) + .append("updateRule", updateRule) + .append("deleteRule", deleteRule) + .build(); + } + } + + private static final class ForeignKeyConstraintsMapFunction implements ResultSetMapFunction { + + private ForeignKeyConstraintsMapFunction() { + } + + @Override + public ForeignKeyConstraint apply(ResultSet resultSet) throws Exception { + return new ForeignKeyConstraint( + resultSet.getString(C_CONSTRAINT_SCHEMA), + resultSet.getString(C_CONSTRAINT_NAME), + resultSet.getString(C_TABLE_SCHEMA), + resultSet.getString(C_TABLE_NAME), + resultSet.getString(C_TABLE_COLUMNS), + resultSet.getString(C_REFERENCED_TABLE_SCHEMA), + resultSet.getString(C_REFERENCED_TABLE_NAME), + resultSet.getString(C_REFERENCED_TABLE_COLUMNS), + resultSet.getString(C_UPDATE_RULE), + resultSet.getString(C_DELETE_RULE) + ); + } + } +} diff --git a/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/OracleForeignKeyManager.java b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/OracleForeignKeyManager.java new file mode 100644 index 00000000..45fc9bce --- /dev/null +++ b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/OracleForeignKeyManager.java @@ -0,0 +1,132 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.commons.lang.ToStringBuilder; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.util.List; +import java.util.Objects; + +import static com.github.mjeanroy.dbunit.commons.lang.PreConditions.notEmpty; +import static com.github.mjeanroy.dbunit.core.jdbc.JdbcUtils.executeQuery; + +final class OracleForeignKeyManager extends AbstractJdbcDropCreateForeignKeyManager { + + private static final String C_CONSTRAINT_NAME = "CONSTRAINT_NAME"; + private static final String C_TABLE_NAME = "TABLE_NAME"; + + private static final ForeignKeyMapFunction mapFunction = new ForeignKeyMapFunction(); + + @Override + List introspectForeignKeys(Connection connection) { + String query = String.format( + "SELECT" + + " UC.CONSTRAINT_NAME AS %s," + + " UC.TABLE_NAME AS %s " + + "FROM USER_CONSTRAINTS UC " + + "WHERE UC.CONSTRAINT_TYPE = 'R' " + + "AND UC.STATUS = 'ENABLED'", + C_CONSTRAINT_NAME, + C_TABLE_NAME + ); + + return executeQuery( + connection, + query, + mapFunction + ); + } + + @Override + String generateDropForeignKeyQuery(ForeignKey fk) { + return String.format( + "ALTER TABLE %s DISABLE CONSTRAINT %s", + fk.tableName, + fk.constraintName + ); + } + + @Override + String generateAddForeignKeyQuery(ForeignKey fk) { + return String.format( + "ALTER TABLE %s ENABLE CONSTRAINT %s", + fk.tableName, + fk.constraintName + ); + } + + static final class ForeignKey { + private final String constraintName; + private final String tableName; + + private ForeignKey( + String constraintName, + String tableName + ) { + this.constraintName = notEmpty(constraintName, "constraintName must be defined"); + this.tableName = notEmpty(tableName, "tableName must be defined"); + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (o instanceof ForeignKey) { + ForeignKey fk = (ForeignKey) o; + return Objects.equals(constraintName, fk.constraintName) + && Objects.equals(tableName, fk.tableName); + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash(constraintName, tableName); + } + + @Override + public String toString() { + return ToStringBuilder.create(this) + .append("constraintName", constraintName) + .append("tableName", tableName) + .build(); + } + } + + private static final class ForeignKeyMapFunction implements JdbcUtils.ResultSetMapFunction { + + @Override + public ForeignKey apply(ResultSet resultSet) throws Exception { + return new ForeignKey( + resultSet.getString(C_CONSTRAINT_NAME), + resultSet.getString(C_TABLE_NAME) + ); + } + } +} diff --git a/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/PostgresForeignKeyManager.java b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/PostgresForeignKeyManager.java new file mode 100644 index 00000000..e7de83b2 --- /dev/null +++ b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/PostgresForeignKeyManager.java @@ -0,0 +1,167 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.commons.lang.ToStringBuilder; +import com.github.mjeanroy.dbunit.core.jdbc.JdbcUtils.ResultSetMapFunction; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.util.List; +import java.util.Objects; + +import static com.github.mjeanroy.dbunit.commons.lang.PreConditions.notEmpty; +import static com.github.mjeanroy.dbunit.core.jdbc.JdbcUtils.executeQuery; + +final class PostgresForeignKeyManager extends AbstractJdbcDropCreateForeignKeyManager { + + private static final String C_NSPNAME = "nspname"; + private static final String C_RELNAME = "relname"; + private static final String C_CONNAME = "conname"; + private static final String C_CONSTRAINT_DEF = "constraintdef"; + + private static final ForeignKeyMapFunction mapFunction = new ForeignKeyMapFunction(); + + PostgresForeignKeyManager() { + super(); + } + + @Override + List introspectForeignKeys(Connection connection) { + String query = String.format( + "SELECT" + + " nspname AS %s," + + " relname AS %s," + + " conname AS %s," + + " pg_get_constraintdef(pg_constraint.oid) AS %s " + + "FROM pg_constraint " + + "INNER JOIN pg_class ON conrelid=pg_class.oid " + + "INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace " + + "WHERE contype = 'f' " + + "ORDER BY %s, %s", + + // Select + C_NSPNAME, + C_RELNAME, + C_CONNAME, + C_CONSTRAINT_DEF, + + // Order by + C_NSPNAME, + C_RELNAME + ); + + return executeQuery( + connection, + query, + mapFunction + ); + } + + @Override + String generateDropForeignKeyQuery(ForeignKey fk) { + return String.format( + "ALTER TABLE \"%s\".\"%s\" DROP CONSTRAINT \"%s\";", fk.nspname, fk.relname, fk.connname + ); + } + + @Override + String generateAddForeignKeyQuery(ForeignKey fk) { + return String.format( + "ALTER TABLE \"%s\".\"%s\" ADD CONSTRAINT \"%s\" %s;", + fk.nspname, + fk.relname, + fk.connname, + fk.constraintdef + ); + } + + static final class ForeignKey { + private final String nspname; + private final String relname; + private final String connname; + private final String constraintdef; + + private ForeignKey( + String nspname, + String relname, + String connname, + String constraintdef + ) { + this.nspname = notEmpty(nspname, "nspname must be defined"); + this.relname = notEmpty(relname, "relname must be defined"); + this.connname = notEmpty(connname, "connname must be defined"); + this.constraintdef = notEmpty(constraintdef, "constraintdef must be defined"); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (o instanceof ForeignKey) { + ForeignKey fk = (ForeignKey) o; + return Objects.equals(nspname, fk.nspname) + && Objects.equals(relname, fk.relname) + && Objects.equals(connname, fk.connname) + && Objects.equals(constraintdef, fk.constraintdef); + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash(nspname, relname, connname, constraintdef); + } + + @Override + public String toString() { + return ToStringBuilder.create(this) + .append("nspname", nspname) + .append("relname", relname) + .append("connname", connname) + .append("constraintdef", constraintdef) + .build(); + } + } + + private static final class ForeignKeyMapFunction implements ResultSetMapFunction { + + private ForeignKeyMapFunction() { + } + + @Override + public ForeignKey apply(ResultSet resultSet) throws Exception { + return new ForeignKey( + resultSet.getString(C_NSPNAME), + resultSet.getString(C_RELNAME), + resultSet.getString(C_CONNAME), + resultSet.getString(C_CONSTRAINT_DEF) + ); + } + } +} diff --git a/src/main/java/com/github/mjeanroy/dbunit/exception/JdbcException.java b/src/main/java/com/github/mjeanroy/dbunit/exception/JdbcException.java index 17dd1603..508effd7 100644 --- a/src/main/java/com/github/mjeanroy/dbunit/exception/JdbcException.java +++ b/src/main/java/com/github/mjeanroy/dbunit/exception/JdbcException.java @@ -49,12 +49,12 @@ public JdbcException(String message) { } /** - * Wrap {@link java.lang.ClassNotFoundException}. + * Wrap {@link java.lang.Exception}. * * @param message Error message. * @param ex Original Exception. */ - public JdbcException(String message, ClassNotFoundException ex) { + public JdbcException(String message, Exception ex) { super(message, ex); } } diff --git a/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/AbstractForeignKeyManagerTest.java b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/AbstractForeignKeyManagerTest.java new file mode 100644 index 00000000..a82c9fec --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/AbstractForeignKeyManagerTest.java @@ -0,0 +1,77 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.exception.JdbcException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.sql.Connection; +import java.sql.Statement; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +abstract class AbstractForeignKeyManagerTest { + + private JdbcForeignKeyManager manager; + + @BeforeEach + void setUp() { + manager = foreignKeyManager(); + } + + @Test + void it_should_disable_constraints(Connection connection) throws Exception { + manager.disable(connection); + + executeUpdate(connection, "INSERT INTO users_movies (user_id, movie_id) VALUES (1, 1)"); + executeUpdate(connection, "INSERT INTO users (id, name) VALUES (1, 'John Doe')"); + executeUpdate(connection, "INSERT INTO movies (id, title) VALUES (1, 'Start Wars')"); + + manager.enable(connection); + } + + @Test + void it_should_disable_constraints_and_fail_to_re_enable_constraints_if_not_valid(Connection connection) throws Exception { + manager.disable(connection); + + executeUpdate(connection, "INSERT INTO users_movies (user_id, movie_id) VALUES (10, 10)"); + + assertThatThrownBy(() -> manager.enable(connection)) + .isInstanceOf(JdbcException.class) + .hasMessage("Cannot enable foreign key constraints, please check your dataset"); + } + + abstract JdbcForeignKeyManager foreignKeyManager(); + + private static void executeUpdate(Connection connection, String query) { + try (Statement statement = connection.createStatement()) { + statement.executeUpdate(query); + } + catch (Exception ex) { + throw new AssertionError(ex); + } + } +} diff --git a/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MariaDBForeignKeyManagerTest.java b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MariaDBForeignKeyManagerTest.java new file mode 100644 index 00000000..d90c9850 --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MariaDBForeignKeyManagerTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.tests.jupiter.TestContainersTest; + +@TestContainersTest( + image = "mariadb:10", + runInitScripts = true, + resolveConnection = true +) +class MariaDBForeignKeyManagerTest extends AbstractForeignKeyManagerTest { + + @Override + JdbcForeignKeyManager foreignKeyManager() { + return new MariaDBForeignKeyManager(); + } +} diff --git a/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MsSQLForeignKeyManagerTest.java b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MsSQLForeignKeyManagerTest.java new file mode 100644 index 00000000..28a12460 --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MsSQLForeignKeyManagerTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.tests.jupiter.TestContainersTest; + +@TestContainersTest( + image = "mcr.microsoft.com/mssql/server", + runInitScripts = true, + resolveConnection = true +) +class MsSQLForeignKeyManagerTest extends AbstractForeignKeyManagerTest { + + @Override + JdbcForeignKeyManager foreignKeyManager() { + return new MsSQLForeignKeyManager(); + } +} diff --git a/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MySQLForeignKeyManagerTest.java b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MySQLForeignKeyManagerTest.java new file mode 100644 index 00000000..b07f70bc --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MySQLForeignKeyManagerTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.tests.jupiter.TestContainersTest; + +@TestContainersTest( + image = "mysql:5.7", + runInitScripts = true, + resolveConnection = true +) +class MySQLForeignKeyManagerTest extends AbstractForeignKeyManagerTest { + + @Override + JdbcForeignKeyManager foreignKeyManager() { + return new MySQLForeignKeyManager(); + } +} diff --git a/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/OracleForeignKeyManagerTest.java b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/OracleForeignKeyManagerTest.java new file mode 100644 index 00000000..a9ce2a04 --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/OracleForeignKeyManagerTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.tests.jupiter.TestContainersTest; + +@TestContainersTest( + image ="gvenzl/oracle-xe:21", + runInitScripts = true, + resolveConnection = true +) +class OracleForeignKeyManagerTest extends AbstractForeignKeyManagerTest { + + @Override + JdbcForeignKeyManager foreignKeyManager() { + return new OracleForeignKeyManager(); + } +} diff --git a/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/PostgresForeignKeyManagerTest.java b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/PostgresForeignKeyManagerTest.java new file mode 100644 index 00000000..d97e0fc2 --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/PostgresForeignKeyManagerTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015-2021 Mickael Jeanroy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.mjeanroy.dbunit.core.jdbc; + +import com.github.mjeanroy.dbunit.tests.jupiter.TestContainersTest; + +@TestContainersTest( + image = "postgres:13", + runInitScripts = true, + resolveConnection = true +) +class PostgresForeignKeyManagerTest extends AbstractForeignKeyManagerTest { + + @Override + JdbcForeignKeyManager foreignKeyManager() { + return new PostgresForeignKeyManager(); + } +} diff --git a/src/test/java/com/github/mjeanroy/dbunit/tests/jupiter/TestContainersExtension.java b/src/test/java/com/github/mjeanroy/dbunit/tests/jupiter/TestContainersExtension.java index e027fb69..3e41ea95 100644 --- a/src/test/java/com/github/mjeanroy/dbunit/tests/jupiter/TestContainersExtension.java +++ b/src/test/java/com/github/mjeanroy/dbunit/tests/jupiter/TestContainersExtension.java @@ -25,12 +25,17 @@ package com.github.mjeanroy.dbunit.tests.jupiter; import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ExecutionCondition; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.ExtensionContext.Store; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.platform.commons.util.AnnotationUtils; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.GenericContainer; @@ -42,10 +47,13 @@ import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.utility.DockerImageName; +import java.lang.reflect.Parameter; +import java.sql.Connection; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Optional; +import java.util.Properties; import java.util.function.Function; import static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled; @@ -55,10 +63,11 @@ * A custom and simple JUnit Jupiter extension to start and shutdown an embedded database * before all/after all tests. */ -class TestContainersExtension implements BeforeAllCallback, AfterAllCallback, ExecutionCondition { +class TestContainersExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ExecutionCondition, ParameterResolver { private static final Namespace NAMESPACE = Namespace.create(TestContainersExtension.class); private static final String CONTAINER_KEY = "container"; + private static final String CONNECTION_KEY = "connection"; private static final String PROP_KEY = "prop"; private static final Map>> containers; @@ -80,15 +89,24 @@ public void beforeAll(ExtensionContext context) { } @Override - public void afterAll(ExtensionContext context) { + public void afterAll(ExtensionContext context) throws Exception { try { - shutdown(context); + closeConnection(context); } finally { - getStore(context).remove(CONTAINER_KEY); + shutdownAndClean(context); } } + @Override + public void beforeEach(ExtensionContext context) { + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + closeConnection(context); + } + @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { return isDockerAvailable() ? @@ -96,11 +114,76 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con disabled("Docker is not available"); } + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { + Parameter parameter = parameterContext.getParameter(); + Class parameterClass = parameter.getType(); + + if (JdbcDatabaseContainer.class.isAssignableFrom(parameterClass)) { + return true; + } + + if (Connection.class.isAssignableFrom(parameterClass)) { + return findAnnotation(extensionContext).resolveConnection(); + } + + return false; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { + Store store = getStore(extensionContext); + JdbcDatabaseContainer dbContainer = store.get(CONTAINER_KEY, JdbcDatabaseContainer.class); + + Parameter parameter = parameterContext.getParameter(); + Class parameterClass = parameter.getType(); + if (JdbcDatabaseContainer.class.isAssignableFrom(parameterClass)) { + return dbContainer; + } + + if (Connection.class.isAssignableFrom(parameterClass)) { + return resolveConnection(store, dbContainer); + } + + throw new IllegalStateException( + "Cannot resolve parameter of type: " + parameterClass + ); + } + + private static void shutdownAndClean(ExtensionContext context) { + try { + shutdown(context); + } + finally { + getStore(context).remove(CONTAINER_KEY); + } + } + + private static void closeConnection(ExtensionContext context) throws Exception { + Store store = getStore(context); + Connection connection = store.remove(CONNECTION_KEY, Connection.class); + if (connection != null) { + connection.close(); + } + } + + private static Connection resolveConnection(Store store, JdbcDatabaseContainer dbContainer) { + Connection currentConnection = store.get(CONNECTION_KEY, Connection.class); + if (currentConnection != null) { + throw new IllegalStateException( + "Cannot create new connection, existing one already exists" + ); + } + + return createConnection(dbContainer); + } + @SuppressWarnings("rawtypes") - private void initialize(ExtensionContext context) { + private static void initialize(ExtensionContext context) { TestContainersTest annotation = findAnnotation(context); String image = annotation.image(); - JdbcDatabaseContainer container = startContainer(image); + boolean runInitScripts = annotation.runInitScripts(); + JdbcDatabaseContainer container = startContainer(image, runInitScripts); getStore(context).put(CONTAINER_KEY, container); @@ -123,7 +206,7 @@ private static TestContainersTest findAnnotation(ExtensionContext context) { } @SuppressWarnings("rawtypes") - private void shutdown(ExtensionContext context) { + private static void shutdown(ExtensionContext context) { try (GenericContainer container = getStore(context).remove(CONTAINER_KEY, GenericContainer.class)) { if (container != null) { container.stop(); @@ -141,8 +224,13 @@ private static Store getStore(ExtensionContext context) { } @SuppressWarnings("rawtypes") - private static JdbcDatabaseContainer startContainer(String image) { - JdbcDatabaseContainer container = getContainer(image).withInitScript("sql/schema.sql"); + private static JdbcDatabaseContainer startContainer(String image, boolean runInitScripts) { + JdbcDatabaseContainer container = getContainer(image); + + if (runInitScripts) { + container.withInitScript("sql/schema.sql"); + } + container.start(); return container; } @@ -204,4 +292,23 @@ private static boolean isDockerAvailable() { return false; } } + + private static Connection createConnection(JdbcDatabaseContainer container) { + try { + return container.getJdbcDriverInstance().connect( + container.getJdbcUrl(), + jdbcProperties(container) + ); + } + catch (Exception ex) { + throw new AssertionError(ex); + } + } + + private static Properties jdbcProperties(JdbcDatabaseContainer container) { + Properties properties = new Properties(); + properties.setProperty("user", container.getUsername()); + properties.setProperty("password", container.getPassword()); + return properties; + } } diff --git a/src/test/java/com/github/mjeanroy/dbunit/tests/jupiter/TestContainersTest.java b/src/test/java/com/github/mjeanroy/dbunit/tests/jupiter/TestContainersTest.java index 1799310f..b7dd4b64 100644 --- a/src/test/java/com/github/mjeanroy/dbunit/tests/jupiter/TestContainersTest.java +++ b/src/test/java/com/github/mjeanroy/dbunit/tests/jupiter/TestContainersTest.java @@ -40,6 +40,10 @@ String image(); + boolean runInitScripts() default true; + + boolean resolveConnection() default false; + String urlProperty() default "tc.jdbcUrl"; String usernameProperty() default "tc.username"; diff --git a/src/test/resources/sql/schema.sql b/src/test/resources/sql/schema.sql index 6a71a975..28a82d27 100644 --- a/src/test/resources/sql/schema.sql +++ b/src/test/resources/sql/schema.sql @@ -42,4 +42,4 @@ CREATE TABLE users_movies_events ( id INT PRIMARY KEY, event VARCHAR(200), FOREIGN KEY (user_id, movie_id) REFERENCES users_movies (user_id, movie_id) ON DELETE CASCADE -); +); \ No newline at end of file diff --git a/start-db.sh b/start-db.sh new file mode 100755 index 00000000..2ef55f87 --- /dev/null +++ b/start-db.sh @@ -0,0 +1,126 @@ +#!/bin/bash + +## +# The MIT License (MIT) +# +# Copyright (c) 2015-2021 Mickael Jeanroy +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +## + +RED="\033[0;31m" +GREEN="\033[0;32m" +YELLOW="\033[0;33m" +RESET_COLORS="\033[0m" + +function start_mssql_server { + port="1433" + password="Azerty123!" + image="mcr.microsoft.com/mssql/server" + + echo -e "${GREEN}Pulling image: ${image}${RESET_COLORS}" + docker pull "${image}" + + echo "" + echo -e "${GREEN}Starting MsSQL server${RESET_COLORS}" + echo -e "${YELLOW} Port: ${port}${RESET_COLORS}" + echo -e "${YELLOW} Password: ${password}${RESET_COLORS}" + echo "" + + docker run --rm \ + -p "${port}:1433" \ + -e 'ACCEPT_EULA=Y' \ + -e "SA_PASSWORD=${password}" \ + "${image}" +} + +function start_mysql { + version=$1 + + if [ "$version" == "" ]; then + version="5.7" + fi + + port="3306" + db_name="test" + user="test" + password="Azerty123!" + image="mysql:${version}" + + echo -e "${GREEN}Pulling image: ${image}${RESET_COLORS}" + docker pull ${image} + + echo "" + echo -e "${GREEN}Starting MySQL server${RESET_COLORS}" + echo -e "${YELLOW} Database: ${db_name}${RESET_COLORS}" + echo -e "${YELLOW} Port: ${port}${RESET_COLORS}" + echo -e "${YELLOW} User: ${user}${RESET_COLORS}" + echo -e "${YELLOW} Password: ${password}${RESET_COLORS}" + echo "" + + docker run --rm \ + -p "${port}:3306" \ + -e "MYSQL_ROOT_PASSWORD=${password}" \ + -e "MYSQL_DATABASE=${db_name}" \ + -e "MYSQL_USER=${user}" \ + -e "MYSQL_PASSWORD=${password}" \ + "${image}" +} + + +function start_oracle { + db_name="test" + port="1521" + app_user="test" + password="Azerty123!" + image="gvenzl/oracle-xe" + + echo -e "${GREEN}Pulling image: ${image}${RESET_COLORS}" + docker pull ${image} + + echo "" + echo -e "${GREEN}Starting OracleXE server${RESET_COLORS}" + echo -e "${YELLOW} Database: ${db_name}${RESET_COLORS}" + echo -e "${YELLOW} Port: ${port}${RESET_COLORS}" + echo -e "${YELLOW} System user: system${RESET_COLORS}" + echo -e "${YELLOW} App user: ${app_user}${RESET_COLORS}" + echo -e "${YELLOW} Password: ${password}${RESET_COLORS}" + echo "" + + docker run --rm \ + -p "${port}:1521" \ + -e "ORACLE_DATABASE=${db_name}" \ + -e "APP_USER=${app_user}" \ + -e "APP_USER_PASSWORD=${password}" \ + -e "ORACLE_PASSWORD=${password}" \ + "${image}" +} + +db_engine=$1 + +if [ "$db_engine" == "mssql" ]; then + start_mssql_server +elif [ "$db_engine" == "mysql" ]; then + start_mysql $2 +elif [ "$db_engine" == "oracle" ]; then + start_oracle +else + echo -e "${RED}Error: Unknown database engine: $db_engine${RESET_COLORS}" + exit -1 +fi