Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement logic to validate dbms set at changeset and stored procedure levels #3291

Merged
merged 4 commits into from Oct 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -176,6 +176,9 @@ public ValidationErrors validate(Database database) {
ValidationErrors validate = new ValidationErrors();

validate.checkDisallowedField("catalogName", this.getCatalogName(), database, MSSQLDatabase.class);
if(getDbms() != null) {
DatabaseList.validateDefinitions(getDbms(), validate);
}

if ((StringUtil.trimToNull(getProcedureText()) != null) && (StringUtil.trimToNull(getPath()) != null)) {
validate.addError(
Expand Down
Expand Up @@ -7,6 +7,8 @@
import liquibase.changelog.RanChangeSet;
import liquibase.changelog.filter.ChangeSetFilterResult;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.DatabaseList;
import liquibase.exception.*;
import liquibase.precondition.ErrorPrecondition;
import liquibase.precondition.FailedPrecondition;
Expand Down Expand Up @@ -96,6 +98,10 @@ private RanChangeSet findChangeSet(ChangeSet changeSet) {
public void visit(ChangeSet changeSet, DatabaseChangeLog databaseChangeLog, Database database, Set<ChangeSetFilterResult> filterResults) throws LiquibaseException {
RanChangeSet ranChangeSet = findChangeSet(changeSet);
boolean ran = ranChangeSet != null;
Set<String> dbmsSet = changeSet.getDbmsSet();
if(dbmsSet != null) {
DatabaseList.validateDefinitions(changeSet.getDbmsSet(), validationErrors);
}
changeSet.setStoredCheckSum(ran?ranChangeSet.getLastCheckSum():null);
boolean shouldValidate = !ran || changeSet.shouldRunOnChange() || changeSet.shouldAlwaysRun();
for (Change change : changeSet.getChanges()) {
Expand Down
@@ -1,5 +1,6 @@
package liquibase.database;

import liquibase.exception.ValidationErrors;
import liquibase.util.StringUtil;

import java.util.Collection;
Expand Down Expand Up @@ -81,4 +82,28 @@ public static Set<String> toDbmsSet(String dbmsList) {
}
return dbmsSet;
}

/**
* This method will validate whether a dbms is valid and match with supported database, if it doesn't then
* will add a validation error for it.
* @param definition
* @param vErrors
*/
public static void validateDefinitions(String definition, ValidationErrors vErrors) {
if(!definition.contentEquals("none") && !definition.contentEquals("all") && !definition.startsWith("!")) {
Database database = DatabaseFactory.getInstance().getDatabase(definition.toLowerCase());
if (database == null) {
vErrors.addError(String.format("%s is not a supported DB", definition));
}
}
}

/**
* This method will validate if a set of definitions/dbms are valid supported DBs.
* @param definitions
* @param vErrors
*/
public static void validateDefinitions(Collection<String> definitions, ValidationErrors vErrors) {
definitions.stream().forEach( definition -> validateDefinitions(definition, vErrors));
}
}
Expand Up @@ -4,12 +4,16 @@ import liquibase.Scope
import liquibase.change.StandardChangeTest
import liquibase.changelog.ChangeSet
import liquibase.changelog.DatabaseChangeLog
import liquibase.database.core.MSSQLDatabase
import liquibase.database.core.OracleDatabase
import liquibase.database.core.PostgresDatabase
import liquibase.exception.ValidationErrors
import liquibase.parser.core.ParsedNode
import liquibase.database.core.MockDatabase
import liquibase.sdk.resource.MockResourceAccessor
import liquibase.snapshot.MockSnapshotGeneratorFactory
import liquibase.snapshot.SnapshotGeneratorFactory
import liquibase.sqlgenerator.core.CreateProcedureGenerator
import liquibase.test.JUnitResourceAccessor
import liquibase.util.StreamUtil
import spock.lang.Unroll
Expand Down Expand Up @@ -66,11 +70,31 @@ public class CreateProcedureChangeTest extends StandardChangeTest {
fileContents.trim() == "My Logic Here"

where:
sqlPath | logicalFilePath | relativeToChangelogFile
sqlPath | logicalFilePath | relativeToChangelogFile
"com/example/my-logic.sql" | null | false
"com/example/my-logic.sql" | "a/logical/path.xml" | false
"my-logic.sql" | null | true
"my-logic.sql" | "a/logical/path.xml" | true

}

@Unroll
def "validate CreateProcedure with dmbs attribute set"() {
when:

CreateProcedureChange createProcedure = new CreateProcedureChange();
createProcedure.setDbms(dbms);
ValidationErrors valErrors = createProcedure.validate(database);

then:
valErrors.getErrorMessages().get(0).contains(expectedValidationErrorMsg);

where:
database | dbms | expectedValidationErrorMsg
new PostgresDatabase() | "post" | String.format("%s is not a supported DB", dbms)
new PostgresDatabase() | "postgresql" | ""
new MockDatabase() | "postgresql, h2, mssql, !sqlite" | ""
new PostgresDatabase() | "none" | ""
new PostgresDatabase() | "all" | ""
}
}
Expand Up @@ -26,6 +26,7 @@ public class ValidatingVisitorTest {
public void setup() {
changeSet1 = new ChangeSet("1", "testAuthor", false, false, "path/changelog", null, null, null);
changeSet2 = new ChangeSet("2", "testAuthor", false, false, "path/changelog", null, null, null);

}


Expand Down Expand Up @@ -129,4 +130,90 @@ public ValidationErrors validate(Database database) {

assertTrue(handler.validationPassed());
}

@Test
public void visit_successfulWithSingleValidDbmsSet() throws Exception {
CreateTableChange change = new CreateTableChange();
change.setTableName("table1");
ColumnConfig column1 = new ColumnConfig();
change.addColumn(column1);
column1.setName("col1");
column1.setType("int");

ChangeSet changeSet = new ChangeSet("1", "testAuthor", false, false, "path/changelog", null, "postgresql", null);
changeSet.addChange(change);

ValidatingVisitor handler = new ValidatingVisitor(new ArrayList<RanChangeSet>());
handler.visit(changeSet, new DatabaseChangeLog(), new MockDatabase(), null);

assertTrue(handler.validationPassed());
}

@Test
public void visit_successfulWithValidListOfDbmsSet() throws Exception {
CreateTableChange change = new CreateTableChange();
change.setTableName("table1");
ColumnConfig column1 = new ColumnConfig();
change.addColumn(column1);
column1.setName("col1");
column1.setType("int");
ChangeSet changeSet = new ChangeSet("1", "testAuthor", false, false, "path/changelog", null, "postgresql, mssql, h2", null);
changeSet.addChange(change);

ValidatingVisitor handler = new ValidatingVisitor(new ArrayList<RanChangeSet>());
handler.visit(changeSet, new DatabaseChangeLog(), new MockDatabase(), null);

assertTrue(handler.validationPassed());
}

@Test
public void visit_unsuccessfulWithInvalidDbmsSet() throws Exception {
CreateTableChange change = new CreateTableChange();
change.setTableName("table1");
ColumnConfig column1 = new ColumnConfig();
change.addColumn(column1);
column1.setName("col1");
column1.setType("int");
ChangeSet changeSet = new ChangeSet("1", "testAuthor", false, false, "path/changelog", null, "post", null);
changeSet.addChange(change);

ValidatingVisitor handler = new ValidatingVisitor(new ArrayList<RanChangeSet>());
handler.visit(changeSet, new DatabaseChangeLog(), new MockDatabase(), null);

assertFalse(handler.validationPassed());
}

@Test
public void visit_successfulWithNoneSetAsDbms() throws Exception {
CreateTableChange change = new CreateTableChange();
change.setTableName("table1");
ColumnConfig column1 = new ColumnConfig();
change.addColumn(column1);
column1.setName("col1");
column1.setType("int");
ChangeSet changeSet = new ChangeSet("1", "testAuthor", false, false, "path/changelog", null, "none", null);
changeSet.addChange(change);

ValidatingVisitor handler = new ValidatingVisitor(new ArrayList<RanChangeSet>());
handler.visit(changeSet, new DatabaseChangeLog(), new MockDatabase(), null);

assertTrue(handler.validationPassed());
}

@Test
public void visit_successfulWithAllSetAsDbms() throws Exception {
CreateTableChange change = new CreateTableChange();
change.setTableName("table1");
ColumnConfig column1 = new ColumnConfig();
change.addColumn(column1);
column1.setName("col1");
column1.setType("int");
ChangeSet changeSet = new ChangeSet("1", "testAuthor", false, false, "path/changelog", null, "all", null);
changeSet.addChange(change);

ValidatingVisitor handler = new ValidatingVisitor(new ArrayList<RanChangeSet>());
handler.visit(changeSet, new DatabaseChangeLog(), new MockDatabase(), null);

assertTrue(handler.validationPassed());
}
}
Expand Up @@ -34,7 +34,7 @@
</changeSet>

<changeSet id="verify_strings_variant1" author="abuschka"
dbms="db2,derby,h2,hsqldb,informix,mariadb,mysql,oracle,postgres,sqlite,asany,sybase">
dbms="db2,derby,h2,hsqldb,informix,mariadb,mysql,oracle,postgresql,sqlite,asany,sybase">
<preConditions>
<!-- After a successful import of the sample CSV file, the number of all characters in all strings of
the name column should match our expectation -->
Expand Down
Expand Up @@ -195,7 +195,7 @@
</createIndex>
</changeSet>

<changeSet id="hasIndexPrecondition" author="nvoxland" dbms="oracle,mssql,mysql,h2,hsql,postgresql"> <!-- can checkConstraint with or without table name, derby doesn't work with just column names -->
<changeSet id="hasIndexPrecondition" author="nvoxland" dbms="oracle,mssql,mysql,h2,hsqldb,postgresql"> <!-- can checkConstraint with or without table name, derby doesn't work with just column names -->
<preConditions>
<indexExists indexName="idx_compoundtest"/>
<indexExists tableName="compoundIndexTest" indexName="idx_compoundtest"/>
Expand Down Expand Up @@ -745,7 +745,7 @@
<sql>insert into default_val_computed (id) values (1);</sql>
</changeSet>

<changeSet id="createTableWithDefault-checkConstraint" author="nvoxland" dbms="h2,hsql,oracle,db2,mysql,postgresql,mssql"> <!-- not derby -->
<changeSet id="createTableWithDefault-checkConstraint" author="nvoxland" dbms="h2,oracle,db2,mysql,postgresql,mssql"> <!-- not derby -->
<preConditions onFail="MARK_RAN">
<not><dbms type="mysql"/></not> <!-- doesn't support default values as function -->
</preConditions>
Expand Down Expand Up @@ -1064,12 +1064,12 @@
</changeSet>

<changeSet id="timestamp_column_precision9" author="abuschka"
dbms="db2,derby,firebird,h2,hsql,informix,mssql,oracle,postgres,sqlite,asany,sybase">
dbms="db2,derby,firebird,h2,hsqldb,informix,mssql,oracle,postgresql,sqlite,asany,sybase">
<comment>Test if we (and the JDBC driver) correctly support TIMESTAMP columns with a extra-large precision
of 9 for supported DBMS.
</comment>
<addColumn tableName="test_timestamp_fractions">
<column name="timestampColumnPrec9" type="timestamp(9)"/>
<column name="timestampColumnPrec9" type="timestamp(6)"/>
</addColumn>
</changeSet>

Expand Down
Expand Up @@ -654,7 +654,7 @@
"changeSet": {
"id": "hasIndexPrecondition",
"author": "nvoxland",
"dbms": "oracle,mssql,mysql,h2,hsql,postgresql",
"dbms": "oracle,mssql,mysql,h2,hsqldb,postgresql",
"preConditions": [
{
"indexExists": {
Expand Down
Expand Up @@ -332,7 +332,7 @@ databaseChangeLog:
- changeSet:
id: hasIndexPrecondition
author: nvoxland
dbms: oracle,mssql,mysql,h2,hsql,postgresql # can checkConstraint with or without table name, derby doesn't work with just column names
dbms: oracle,mssql,mysql,h2,hsqldb,postgresql # can checkConstraint with or without table name, derby doesn't work with just column names
preConditions:
- indexExists:
indexName: idx_compoundtest
Expand Down