From 3ae8aea8571ee73952da80836ffd60fc0fb9304d Mon Sep 17 00:00:00 2001 From: Mickael Jeanroy Date: Thu, 14 Sep 2023 08:47:59 +0200 Subject: [PATCH] feat: disable foreign keys during dataset setup/teardown - Add postgres foreign key manager - Add mysql foreign key manager - Add mariadb foreign key manager - Add mssql foreign key manager - Add oracle foreign key manager --- .../dbunit/commons/lang/PreConditions.java | 21 ++ .../dbunit/commons/lang/ToStringBuilder.java | 11 + ...stractJdbcDropCreateForeignKeyManager.java | 128 ++++++++ .../core/jdbc/JdbcForeignKeyManager.java | 34 +++ .../mjeanroy/dbunit/core/jdbc/JdbcUtils.java | 63 ++++ .../core/jdbc/MariaDBForeignKeyManager.java | 48 +++ .../core/jdbc/MsSQLForeignKeyManager.java | 287 ++++++++++++++++++ .../core/jdbc/MySQLForeignKeyManager.java | 255 ++++++++++++++++ .../core/jdbc/OracleForeignKeyManager.java | 132 ++++++++ .../core/jdbc/PostgresForeignKeyManager.java | 167 ++++++++++ .../dbunit/exception/JdbcException.java | 4 +- .../jdbc/AbstractForeignKeyManagerTest.java | 102 +++++++ .../jdbc/MariaDBForeignKeyManagerTest.java | 39 +++ .../core/jdbc/MsSQLForeignKeyManagerTest.java | 39 +++ .../core/jdbc/MySQLForeignKeyManagerTest.java | 39 +++ .../jdbc/OracleForeignKeyManagerTest.java | 39 +++ .../jdbc/PostgresForeignKeyManagerTest.java | 39 +++ .../jupiter/TestContainersExtension.java | 30 +- .../tests/jupiter/TestContainersTest.java | 2 + src/test/resources/sql/schema.sql | 2 +- start-db.sh | 126 ++++++++ 21 files changed, 1600 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/github/mjeanroy/dbunit/core/jdbc/AbstractJdbcDropCreateForeignKeyManager.java create mode 100644 src/main/java/com/github/mjeanroy/dbunit/core/jdbc/JdbcForeignKeyManager.java create mode 100644 src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MariaDBForeignKeyManager.java create mode 100644 src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MsSQLForeignKeyManager.java create mode 100644 src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MySQLForeignKeyManager.java create mode 100644 src/main/java/com/github/mjeanroy/dbunit/core/jdbc/OracleForeignKeyManager.java create mode 100644 src/main/java/com/github/mjeanroy/dbunit/core/jdbc/PostgresForeignKeyManager.java create mode 100644 src/test/java/com/github/mjeanroy/dbunit/core/jdbc/AbstractForeignKeyManagerTest.java create mode 100644 src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MariaDBForeignKeyManagerTest.java create mode 100644 src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MsSQLForeignKeyManagerTest.java create mode 100644 src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MySQLForeignKeyManagerTest.java create mode 100644 src/test/java/com/github/mjeanroy/dbunit/core/jdbc/OracleForeignKeyManagerTest.java create mode 100644 src/test/java/com/github/mjeanroy/dbunit/core/jdbc/PostgresForeignKeyManagerTest.java create mode 100755 start-db.sh 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/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..b047c1a4 --- /dev/null +++ b/src/main/java/com/github/mjeanroy/dbunit/core/jdbc/MsSQLForeignKeyManager.java @@ -0,0 +1,287 @@ +/** + * 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_IS_NOT_TRUSTED = "is_not_trusted"; + private static final String C_IS_NOT_FOR_REPLICATION = "is_not_for_replication"; + 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_table_columns"; + private static final String C_DELETE_RULE = "delete_rule"; + private static final String C_UPDATE_RULE = "update_rule"; + + private static final ForeignKeyConstraintsMapFunction mapFunction = new ForeignKeyConstraintsMapFunction(); + + MsSQLForeignKeyManager() { + } + + @Override + List introspectForeignKeys(Connection connection) { + String query = String.format( + "SELECT" + + " QUOTENAME(fk.name) AS %s," + + " fk.is_not_trusted AS %s," + + " fk.is_not_for_replication AS %s," + + " QUOTENAME(cs.name) AS %s," + + " QUOTENAME(ct.name) AS %s," + + " STUFF(" + + " (" + + " SELECT ',' + QUOTENAME(c.name)" + + " FROM sys.columns AS c" + + " INNER JOIN sys.foreign_key_columns AS fkc" + + " ON fkc.parent_column_id = c.column_id" + + " AND fkc.parent_object_id = c.object_id" + + " WHERE fkc.constraint_object_id = fk.object_id" + + " ORDER BY fkc.constraint_column_id" + + " FOR XML PATH(N''), TYPE" + + " ).value(N'.[1]', N'nvarchar(max)')," + + " 1, 1, N''" + + " ) AS %s," + + " QUOTENAME(rs.name) AS %s," + + " QUOTENAME(rt.name) AS %s," + + " STUFF(" + + " (" + + " SELECT ',' + QUOTENAME(c.name)" + + " FROM sys.columns AS c" + + " INNER JOIN sys.foreign_key_columns AS fkc" + + " ON fkc.referenced_column_id = c.column_id" + + " AND fkc.referenced_object_id = c.[object_id]" + + " WHERE fkc.constraint_object_id = fk.[object_id]" + + " ORDER BY fkc.constraint_column_id" + + " FOR XML PATH(N''), TYPE" + + " ).value(N'.[1]', N'nvarchar(max)')," + + " 1, 1, N''" + + " ) AS %s," + + " CASE fk.delete_referential_action" + + " WHEN 0 THEN 'NO ACTION'" + + " WHEN 1 THEN 'CASCADE'" + + " WHEN 2 THEN 'SET NULL'" + + " WHEN 3 THEN 'SET DEFAULT'" + + " END AS %s," + + " CASE fk.update_referential_action" + + " WHEN 0 THEN 'NO ACTION'" + + " WHEN 1 THEN 'CASCADE'" + + " WHEN 2 then 'SET NULL'" + + " WHEN 3 THEN 'SET DEFAULT'" + + " END AS %s " + + "FROM sys.foreign_keys AS fk " + + "INNER JOIN sys.tables AS rt ON fk.referenced_object_id = rt.object_id " + + "INNER JOIN sys.schemas AS rs ON rt.schema_id = rs.schema_id " + + "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 rt.is_ms_shipped = 0 " + + "AND ct.is_ms_shipped = 0;", + + C_CONSTRAINT_NAME, + C_IS_NOT_TRUSTED, + C_IS_NOT_FOR_REPLICATION, + C_TABLE_SCHEMA, + C_TABLE_NAME, + C_TABLE_COLUMNS, + C_REFERENCED_TABLE_SCHEMA, + C_REFERENCED_TABLE_NAME, + C_REFERENCED_TABLE_COLUMNS, + C_DELETE_RULE, + C_UPDATE_RULE + ); + + return executeQuery( + connection, + query, + mapFunction + ); + } + + @Override + String generateDropForeignKeyQuery(ForeignKeyConstraint fk) { + return String.format( + "ALTER TABLE %s.%s DROP CONSTRAINT %s;", + fk.tableSchema, + fk.tableName, + fk.constraintName + ); + } + + @Override + String generateAddForeignKeyQuery(ForeignKeyConstraint fk) { + return String.format( + "ALTER TABLE %s.%s %s " + + "ADD CONSTRAINT %s " + + "FOREIGN KEY (%s) REFERENCES %s.%s (%s) " + + "ON UPDATE %s " + + "ON DELETE %s" + + "%s;", + fk.tableSchema, + fk.tableName, + fk.isNotTrusted ? "WITH NOCHECK" : "WITH CHECK", + fk.constraintName, + fk.tableColumns, + fk.referencedTableSchema, + fk.referencedTableName, + fk.referencedTableColumns, + fk.updateRule, + fk.deleteRule, + fk.isNotForReplication ? "NOT FOR REPLICATION" : "" + ); + } + + static final class ForeignKeyConstraint { + private final String constraintName; + private final boolean isNotTrusted; + private final boolean isNotForReplication; + private final String tableSchema; + private final String tableName; + private final String tableColumns; + private final String referencedTableSchema; + private final String referencedTableName; + private final String referencedTableColumns; + private final String deleteRule; + private final String updateRule; + + private ForeignKeyConstraint( + String constraintName, + boolean isNotTrusted, + boolean isNotForReplication, + String tableSchema, + String tableName, + String tableColumns, + String referencedTableSchema, + String referencedTableName, + String referencedTableColumns, + String deleteRule, + String updateRule + ) { + this.constraintName = notEmpty(constraintName, "constraintName must be defined"); + this.isNotTrusted = isNotTrusted; + this.isNotForReplication = isNotForReplication; + this.tableSchema = notEmpty(tableSchema, "tableSchema must be defined"); + this.tableName = notEmpty(tableName, "tableName must be defined"); + this.tableColumns = notEmpty(tableColumns, "tableColumns must be defined"); + this.referencedTableSchema = notEmpty(referencedTableSchema, "referencedTableSchema must be defined"); + this.referencedTableName = notEmpty(referencedTableName, "referencedTableName must be defined"); + this.referencedTableColumns = notEmpty(referencedTableColumns, "referencedTableColumns must be defined"); + this.deleteRule = notEmpty(deleteRule, "deleteRule must be defined"); + this.updateRule = notEmpty(updateRule, "updateRule 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(isNotTrusted, fk.isNotTrusted) + && Objects.equals(isNotForReplication, fk.isNotForReplication) + && Objects.equals(tableSchema, fk.tableSchema) + && Objects.equals(tableName, fk.tableName) + && Objects.equals(tableColumns, fk.tableColumns) + && Objects.equals(referencedTableSchema, fk.referencedTableSchema) + && Objects.equals(referencedTableName, fk.referencedTableName) + && Objects.equals(referencedTableColumns, fk.referencedTableColumns) + && Objects.equals(deleteRule, fk.deleteRule) + && Objects.equals(updateRule, fk.updateRule); + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash( + constraintName, + isNotTrusted, + isNotForReplication, + tableSchema, + tableName, + tableColumns, + referencedTableSchema, + referencedTableName, + referencedTableColumns, + updateRule, + deleteRule + ); + } + + @Override + public String toString() { + return ToStringBuilder.create(this) + .append("constraintName", constraintName) + .append("isNotTrusted", isNotTrusted) + .append("isNotForReplication", isNotForReplication) + .append("tableSchema", tableSchema) + .append("tableName", tableName) + .append("tableColumns", tableColumns) + .append("referencedTableSchema", referencedTableSchema) + .append("referencedTableName", referencedTableName) + .append("referencedTableColumns", referencedTableColumns) + .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_NAME), + resultSet.getBoolean(C_IS_NOT_TRUSTED), + resultSet.getBoolean(C_IS_NOT_FOR_REPLICATION), + 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_DELETE_RULE), + resultSet.getString(C_UPDATE_RULE) + ); + } + } +} 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..a4eb3a4c --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/AbstractForeignKeyManagerTest.java @@ -0,0 +1,102 @@ +/** + * 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 org.testcontainers.containers.JdbcDatabaseContainer; + +import java.sql.Connection; +import java.sql.Statement; +import java.util.Properties; + +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(JdbcDatabaseContainer container) throws Exception { + try (Connection connection = connection(container)) { + 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(JdbcDatabaseContainer container) throws Exception { + try (Connection connection = connection(container)) { + 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); + } + } + + private static Connection connection(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/core/jdbc/MariaDBForeignKeyManagerTest.java b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MariaDBForeignKeyManagerTest.java new file mode 100644 index 00000000..516ebdce --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MariaDBForeignKeyManagerTest.java @@ -0,0 +1,39 @@ +/** + * 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 +) +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..0eca14e3 --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MsSQLForeignKeyManagerTest.java @@ -0,0 +1,39 @@ +/** + * 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 +) +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..841c9659 --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/MySQLForeignKeyManagerTest.java @@ -0,0 +1,39 @@ +/** + * 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 +) +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..daa99b8a --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/OracleForeignKeyManagerTest.java @@ -0,0 +1,39 @@ +/** + * 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 +) +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..fd8cae5b --- /dev/null +++ b/src/test/java/com/github/mjeanroy/dbunit/core/jdbc/PostgresForeignKeyManagerTest.java @@ -0,0 +1,39 @@ +/** + * 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 +) +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..aa3a9594 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 @@ -31,6 +31,9 @@ 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,6 +45,7 @@ import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.utility.DockerImageName; +import java.lang.reflect.Parameter; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -55,7 +59,7 @@ * 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, ExecutionCondition, ParameterResolver { private static final Namespace NAMESPACE = Namespace.create(TestContainersExtension.class); private static final String CONTAINER_KEY = "container"; @@ -96,11 +100,24 @@ 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(); + return JdbcDatabaseContainer.class.isAssignableFrom(parameterClass); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { + return getStore(extensionContext).get(CONTAINER_KEY, JdbcDatabaseContainer.class); + } + @SuppressWarnings("rawtypes") private 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); @@ -141,8 +158,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; } 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..379c7260 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,8 @@ String image(); + boolean runInitScripts() default true; + 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