diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml
index 726a1696c46..5f2274ea738 100644
--- a/.github/workflows/snyk.yml
+++ b/.github/workflows/snyk.yml
@@ -60,7 +60,7 @@ jobs:
SLACK_COLOR: ${{ job.status }} # or a specific color like 'good' or '#ff00ff'
SLACK_MESSAGE: "${{ github.job }}: ${{ job.status }} @here"
SLACK_USERNAME: "liquibot"
- SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
+ SLACK_WEBHOOK: ${{ secrets.SNYK_LIQUIBASE_SLACK_WEBHOOK }}
MSG_MINIMAL: actions url
SLACK_ICON_EMOJI: ':liquibase:'
SLACK_LINK_NAMES: true
diff --git a/liquibase-core/pom.xml b/liquibase-core/pom.xml
index 9755ec4aaf5..0ba213325d6 100644
--- a/liquibase-core/pom.xml
+++ b/liquibase-core/pom.xml
@@ -57,13 +57,13 @@
1.3
test
-
+
org.osgi
org.osgi.core
provided
- 4.3.1
-
+ 5.0.0
+
org.springframework
@@ -155,10 +155,11 @@
org.apache.felix
maven-bundle-plugin
- 3.3.0
+ 5.1.2
org.liquibase.core
+ liquibase.osgi.Activator
javax.activation*;resolution:=optional,
javax.servlet.*;version="[2.6,4)";resolution:=optional,
@@ -167,6 +168,66 @@
org.yaml.snakeyaml.*,
*;resolution:=optional
+
+ osgi.serviceloader; osgi.serviceloader=liquibase.serializer.ChangeLogSerializer,
+ osgi.serviceloader; osgi.serviceloader=liquibase.parser.NamespaceDetails,
+ osgi.serviceloader; osgi.serviceloader=liquibase.database.Database,
+ osgi.serviceloader; osgi.serviceloader=liquibase.change.Change,
+ osgi.serviceloader; osgi.serviceloader=liquibase.database.DatabaseConnection,
+ osgi.serviceloader; osgi.serviceloader=liquibase.precondition.Precondition,
+ osgi.serviceloader; osgi.serviceloader=liquibase.serializer.SnapshotSerializer,
+ osgi.serviceloader; osgi.serviceloader=liquibase.configuration.AutoloadedConfigurations,
+ osgi.serviceloader; osgi.serviceloader=liquibase.diff.DiffGenerator,
+ osgi.serviceloader; osgi.serviceloader=liquibase.lockservice.LockService,
+ osgi.serviceloader; osgi.serviceloader=liquibase.changelog.ChangeLogHistoryService,
+ osgi.serviceloader; osgi.serviceloader=liquibase.datatype.LiquibaseDataType,
+ osgi.serviceloader; osgi.serviceloader=liquibase.configuration.ConfigurationValueProvider,
+ osgi.serviceloader; osgi.serviceloader=liquibase.logging.LogService,
+ osgi.serviceloader; osgi.serviceloader=liquibase.snapshot.SnapshotGenerator,
+ osgi.serviceloader; osgi.serviceloader=liquibase.parser.ChangeLogParser,
+ osgi.serviceloader; osgi.serviceloader=liquibase.servicelocator.ServiceLocator,
+ osgi.serviceloader; osgi.serviceloader=liquibase.diff.compare.DatabaseObjectComparator,
+ osgi.serviceloader; osgi.serviceloader=liquibase.command.LiquibaseCommand,
+ osgi.serviceloader; osgi.serviceloader=liquibase.license.LicenseService,
+ osgi.serviceloader; osgi.serviceloader=liquibase.diff.output.changelog.ChangeGenerator,
+ osgi.serviceloader; osgi.serviceloader=liquibase.executor.Executor,
+ osgi.serviceloader; osgi.serviceloader=liquibase.structure.DatabaseObject,
+ osgi.serviceloader; osgi.serviceloader=liquibase.parser.SnapshotParser,
+ osgi.serviceloader; osgi.serviceloader=liquibase.hub.HubService,
+ osgi.serviceloader; osgi.serviceloader=liquibase.command.CommandStep,
+ osgi.serviceloader; osgi.serviceloader=liquibase.sqlgenerator.SqlGenerator
+
+
+ osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)",
+ osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)",
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.serializer.ChangeLogSerializer)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.parser.NamespaceDetails)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.database.Database)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.change.Change)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.database.DatabaseConnection)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.precondition.Precondition)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.serializer.SnapshotSerializer)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.configuration.AutoloadedConfigurations)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.diff.DiffGenerator)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.lockservice.LockService)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.changelog.ChangeLogHistoryService)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.datatype.LiquibaseDataType)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.configuration.ConfigurationValueProvider)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.logging.LogService)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.snapshot.SnapshotGenerator)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.parser.ChangeLogParser)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.servicelocator.ServiceLocator)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.diff.compare.DatabaseObjectComparator)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.command.LiquibaseCommand)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.license.LicenseService)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.diff.output.changelog.ChangeGenerator)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.executor.Executor)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.structure.DatabaseObject)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.parser.SnapshotParser)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.hub.HubService)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.command.CommandStep)"; cardinality:=multiple,
+ osgi.serviceloader; filter:="(osgi.serviceloader=liquibase.sqlgenerator.SqlGenerator)"; cardinality:=multiple
+
diff --git a/liquibase-core/src/main/java/liquibase/Scope.java b/liquibase-core/src/main/java/liquibase/Scope.java
index 25a5aa3adcb..0910d57134c 100644
--- a/liquibase-core/src/main/java/liquibase/Scope.java
+++ b/liquibase-core/src/main/java/liquibase/Scope.java
@@ -11,6 +11,7 @@
import liquibase.logging.Logger;
import liquibase.logging.core.JavaLogService;
import liquibase.logging.core.LogServiceFactory;
+import liquibase.osgi.Activator;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.ResourceAccessor;
import liquibase.servicelocator.ServiceLocator;
@@ -55,6 +56,7 @@ public enum Attr {
fileEncoding,
databaseChangeLog,
changeSet,
+ osgiPlatform
}
private static ScopeManager scopeManager;
@@ -95,6 +97,7 @@ public static Scope getCurrentScope() {
}
rootScope.values.put(Attr.serviceLocator.name(), serviceLocator);
+ rootScope.values.put(Attr.osgiPlatform.name(), Activator.OSGIContainerChecker.isOsgiPlatform());
}
return scopeManager.getCurrentScope();
}
diff --git a/liquibase-core/src/main/java/liquibase/change/core/AddNotNullConstraintChange.java b/liquibase-core/src/main/java/liquibase/change/core/AddNotNullConstraintChange.java
index c236ab86c12..04bd19a2891 100755
--- a/liquibase-core/src/main/java/liquibase/change/core/AddNotNullConstraintChange.java
+++ b/liquibase-core/src/main/java/liquibase/change/core/AddNotNullConstraintChange.java
@@ -3,15 +3,19 @@
import liquibase.change.*;
import liquibase.database.Database;
import liquibase.database.core.DB2Database;
-import liquibase.database.core.SQLiteDatabase;
+import liquibase.database.core.PostgresDatabase;
import liquibase.database.core.SQLiteDatabase.AlterTableVisitor;
-import liquibase.exception.DatabaseException;
+import liquibase.datatype.DataTypeFactory;
+import liquibase.datatype.LiquibaseDataType;
+import liquibase.datatype.core.BooleanType;
+import liquibase.statement.DatabaseFunction;
import liquibase.statement.SqlStatement;
import liquibase.statement.core.ReorganizeTableStatement;
import liquibase.statement.core.SetNullableStatement;
import liquibase.statement.core.UpdateStatement;
import liquibase.structure.core.Column;
import liquibase.structure.core.Index;
+import liquibase.util.BooleanUtil;
import liquibase.util.StringUtil;
import java.util.ArrayList;
@@ -135,9 +139,41 @@ public void setConstraintName(String constraintName) {
public SqlStatement[] generateStatements(Database database) {
List statements = new ArrayList<>();
- if (defaultNullValue != null) {
+ if (defaultNullValue != null && !defaultNullValue.equalsIgnoreCase("null")) {
+ final String columnDataType = this.getColumnDataType();
+
+ Object finalDefaultNullValue = defaultNullValue;
+ if (columnDataType != null) {
+ final LiquibaseDataType datatype = DataTypeFactory.getInstance().fromDescription(columnDataType, database);
+ if (datatype instanceof BooleanType) {
+ //need to detect a boolean or bit type and handle it correctly sometimes or it is not converted to the correct datatype
+ finalDefaultNullValue = datatype.objectToSql(finalDefaultNullValue, database);
+ if (finalDefaultNullValue.equals("0")) {
+ finalDefaultNullValue = 0;
+ } else if (finalDefaultNullValue.equals("1")) {
+ finalDefaultNullValue = 1;
+ }
+
+ if (columnDataType.toLowerCase().contains("bit")) {
+ if (BooleanUtil.parseBoolean(finalDefaultNullValue.toString())) {
+ finalDefaultNullValue = 1;
+ } else {
+ finalDefaultNullValue = 0;
+ }
+ }
+
+ if (database instanceof PostgresDatabase) {
+ if (finalDefaultNullValue.equals(0)) {
+ finalDefaultNullValue = new DatabaseFunction( "B'0'");
+ } else if (finalDefaultNullValue.equals(1)) {
+ finalDefaultNullValue = new DatabaseFunction( "B'1'");
+ }
+ }
+ }
+ }
+
statements.add(new UpdateStatement(getCatalogName(), getSchemaName(), getTableName())
- .addNewColumnValue(getColumnName(), defaultNullValue)
+ .addNewColumnValue(getColumnName(), finalDefaultNullValue)
.setWhereClause(database.escapeObjectName(getColumnName(), Column.class) +
" IS NULL"));
}
diff --git a/liquibase-core/src/main/java/liquibase/change/custom/CustomChangeWrapper.java b/liquibase-core/src/main/java/liquibase/change/custom/CustomChangeWrapper.java
index 1f4e4f94d9a..f86c0b68025 100644
--- a/liquibase-core/src/main/java/liquibase/change/custom/CustomChangeWrapper.java
+++ b/liquibase-core/src/main/java/liquibase/change/custom/CustomChangeWrapper.java
@@ -14,6 +14,7 @@
import liquibase.util.ObjectUtil;
import java.util.*;
+import liquibase.util.OsgiUtil;
/**
* Adapts CustomChange implementations to the standard change system used by Liquibase.
@@ -69,16 +70,21 @@ public CustomChangeWrapper setClass(String className) throws CustomChangeExcepti
return this;
}
this.className = className;
- try {
+ try {
+ Boolean osgiPlatform = Scope.getCurrentScope().get(Scope.Attr.osgiPlatform, Boolean.class);
+ if (Boolean.TRUE.equals(osgiPlatform)) {
+ customChange = (CustomChange)OsgiUtil.loadClass(className).getConstructor().newInstance();
+ } else {
+ try {
+ customChange = (CustomChange) Class.forName(className, true, Scope.getCurrentScope().getClassLoader()).getConstructor().newInstance();
+ } catch (ClassCastException e) { //fails in Ant in particular
try {
- customChange = (CustomChange) Class.forName(className, true, Scope.getCurrentScope().getClassLoader()).getConstructor().newInstance();
- } catch (ClassCastException e) { //fails in Ant in particular
- try {
- customChange = (CustomChange) Thread.currentThread().getContextClassLoader().loadClass(className).getConstructor().newInstance();
- } catch (ClassNotFoundException e1) {
- customChange = (CustomChange) Class.forName(className).getConstructor().newInstance();
- }
+ customChange = (CustomChange) Thread.currentThread().getContextClassLoader().loadClass(className).getConstructor().newInstance();
+ } catch (ClassNotFoundException e1) {
+ customChange = (CustomChange) Class.forName(className).getConstructor().newInstance();
}
+ }
+ }
} catch (Exception e) {
throw new CustomChangeException(e);
}
@@ -316,7 +322,12 @@ public void customLoadLogic(ParsedNode parsedNode, ResourceAccessor resourceAcce
CustomChange customChange = null;
try {
- customChange = (CustomChange) Class.forName(className, false, Scope.getCurrentScope().getClassLoader()).getConstructor().newInstance();
+ Boolean osgiPlatform = Scope.getCurrentScope().get(Scope.Attr.osgiPlatform, Boolean.class);
+ if (Boolean.TRUE.equals(osgiPlatform)) {
+ customChange = (CustomChange)OsgiUtil.loadClass(className).getConstructor().newInstance();
+ } else {
+ customChange = (CustomChange) Class.forName(className, false, Scope.getCurrentScope().getClassLoader()).getConstructor().newInstance();
+ }
} catch (Exception e) {
throw new UnexpectedLiquibaseException(e);
}
diff --git a/liquibase-core/src/main/java/liquibase/osgi/Activator.java b/liquibase-core/src/main/java/liquibase/osgi/Activator.java
new file mode 100644
index 00000000000..673a53cebe1
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/osgi/Activator.java
@@ -0,0 +1,108 @@
+package liquibase.osgi;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import liquibase.osgi.Activator.LiquibaseBundle;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+
+public class Activator implements BundleActivator, BundleTrackerCustomizer {
+
+ private static final String LIQUIBASE_CUSTOM_CHANGE_WRAPPER_PACKAGES = "Liquibase-Custom-Change-Packages";
+ private BundleTracker bundleTracker;
+ private static final List liquibaseBundles = new CopyOnWriteArrayList<>();
+
+ @Override
+ public void start(final BundleContext bc) throws Exception {
+ OSGIContainerChecker.osgiPlatform();
+ bundleTracker = new BundleTracker<>(bc, Bundle.ACTIVE, this);
+ bundleTracker.open();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ bundleTracker.close();
+ liquibaseBundles.clear();
+ }
+
+ public static List getLiquibaseBundles() {
+ return Collections.unmodifiableList(liquibaseBundles);
+ }
+
+ @Override
+ public LiquibaseBundle addingBundle(Bundle bundle, BundleEvent event) {
+ if (bundle.getBundleId() == 0) {
+ return null;
+ }
+ String customWrapperPackages = (String) bundle.getHeaders().get(LIQUIBASE_CUSTOM_CHANGE_WRAPPER_PACKAGES);
+ if (customWrapperPackages != null) {
+ LiquibaseBundle lb = new LiquibaseBundle(bundle, customWrapperPackages);
+ liquibaseBundles.add(lb);
+ return lb;
+ }
+ return null;
+ }
+
+ @Override
+ public void modifiedBundle(Bundle bundle, BundleEvent event, LiquibaseBundle liquibaseBundle) {
+ // nothing to do
+ }
+
+ @Override
+ public void removedBundle(Bundle bundle, BundleEvent event, LiquibaseBundle liquibaseBundle) {
+ if (liquibaseBundle != null) {
+ liquibaseBundles.remove(liquibaseBundle);
+ }
+ }
+
+ public static class LiquibaseBundle {
+
+ private final Bundle bundle;
+ private final List allowedPackages;
+
+ public LiquibaseBundle(Bundle bundle, String allowedPackages) {
+ if (bundle == null) {
+ throw new IllegalArgumentException("bundle cannot be empty");
+ }
+ if (allowedPackages == null || allowedPackages.isEmpty()) {
+ throw new IllegalArgumentException("packages cannot be empty");
+ }
+ this.bundle = bundle;
+ this.allowedPackages = Collections.unmodifiableList(Arrays.asList(allowedPackages.split(",")));
+ }
+
+ public Bundle getBundle() {
+ return bundle;
+ }
+
+ public boolean allowedAllPackages() {
+ return allowedPackages.size() == 1
+ && "*".equals(allowedPackages.get(0));
+ }
+
+ public List getAllowedPackages() {
+ return allowedPackages;
+ }
+
+ }
+
+ public static class OSGIContainerChecker {
+
+ private static volatile boolean osgiPlatform = false;
+
+ public static boolean isOsgiPlatform() {
+ return osgiPlatform;
+ }
+
+ static void osgiPlatform() {
+ osgiPlatform = true;
+ }
+ }
+
+}
diff --git a/liquibase-core/src/main/java/liquibase/util/LiquibaseUtil.java b/liquibase-core/src/main/java/liquibase/util/LiquibaseUtil.java
index 8b56d2ca485..de89b1868c8 100644
--- a/liquibase-core/src/main/java/liquibase/util/LiquibaseUtil.java
+++ b/liquibase-core/src/main/java/liquibase/util/LiquibaseUtil.java
@@ -1,6 +1,8 @@
package liquibase.util;
import liquibase.Scope;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
import java.io.IOException;
import java.io.InputStream;
@@ -54,19 +56,37 @@ public static String getBuildNumber() {
// the jar file.
private static String getBuildInfo(String propertyId) {
if (liquibaseBuildProperties == null) {
- try {
- liquibaseBuildProperties = new Properties();
- final Enumeration propertiesUrls = Scope.getCurrentScope().getClassLoader().getResources("liquibase.build.properties");
- while (propertiesUrls.hasMoreElements()) {
- final URL url = propertiesUrls.nextElement();
- try (InputStream buildProperties = url.openStream()) {
+ Boolean osgiPlatform = Scope.getCurrentScope().get(Scope.Attr.osgiPlatform, Boolean.class);
+ if (Boolean.TRUE.equals(osgiPlatform)) {
+ Bundle bundle = FrameworkUtil.getBundle(LiquibaseUtil.class);
+ URL propURL = bundle.getEntry("liquibase.build.properties");
+ if (propURL == null) {
+ Scope.getCurrentScope().getLog(LiquibaseUtil.class).severe("Cannot read liquibase.build.properties");
+ } else {
+ try (InputStream buildProperties = propURL.openStream()) {
+ liquibaseBuildProperties = new Properties();
if (buildProperties != null) {
liquibaseBuildProperties.load(buildProperties);
}
+ } catch (IOException e) {
+ Scope.getCurrentScope().getLog(LiquibaseUtil.class).severe("Cannot read liquibase.build.properties", e);
}
}
- } catch (IOException e) {
- Scope.getCurrentScope().getLog(LiquibaseUtil.class).severe("Cannot read liquibase.build.properties", e);
+ } else {
+ try {
+ liquibaseBuildProperties = new Properties();
+ final Enumeration propertiesUrls = Scope.getCurrentScope().getClassLoader().getResources("liquibase.build.properties");
+ while (propertiesUrls.hasMoreElements()) {
+ final URL url = propertiesUrls.nextElement();
+ try (InputStream buildProperties = url.openStream()) {
+ if (buildProperties != null) {
+ liquibaseBuildProperties.load(buildProperties);
+ }
+ }
+ }
+ } catch (IOException e) {
+ Scope.getCurrentScope().getLog(LiquibaseUtil.class).severe("Cannot read liquibase.build.properties", e);
+ }
}
}
diff --git a/liquibase-core/src/main/java/liquibase/util/OsgiUtil.java b/liquibase-core/src/main/java/liquibase/util/OsgiUtil.java
new file mode 100644
index 00000000000..71a39077f77
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/util/OsgiUtil.java
@@ -0,0 +1,65 @@
+package liquibase.util;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import liquibase.osgi.Activator;
+import liquibase.osgi.Activator.LiquibaseBundle;
+
+public final class OsgiUtil {
+
+ private OsgiUtil() {
+ }
+
+ /**
+ * try to load a class under OSGI environment. It will try to load the class
+ * from all liquibase bundles registered via
+ * {@link Activator Activator}
+ *
+ * @param
+ * @param className name of class
+ * @return
+ * @throws ClassNotFoundException
+ */
+ public static Class loadClass(String className) throws ClassNotFoundException {
+ List liquibaseBundles = Activator.getLiquibaseBundles();
+ for (LiquibaseBundle lb : liquibaseBundles) {
+ try {
+ Class clazz = (Class) lb.getBundle().loadClass(className);
+ if (!isClassAllowed(lb, clazz)) {
+ throw new ClassNotFoundException("Class is not allowed to load, class:" + className + " bundles:"
+ + liquibaseBundles.stream().map(i -> i.getBundle().getSymbolicName())
+ .collect(Collectors.joining(",")));
+ }
+ return clazz;
+ } catch (ClassNotFoundException ex) {
+ // nothing to do
+ }
+ }
+ throw new ClassNotFoundException("Cannot find class:" + className + " bundles:"
+ + liquibaseBundles.stream().map(i -> i.getBundle().getSymbolicName())
+ .collect(Collectors.joining(",")));
+ }
+
+ /**
+ *
+ * @param clazz
+ * @return true is a class is allowed
+ * @throws java.lang.ClassNotFoundException
+ */
+ private static boolean isClassAllowed(LiquibaseBundle liquibaseBundle, Class clazz) {
+ if (liquibaseBundle.allowedAllPackages()) {
+ return true;
+ }
+ for (String allowedPackage : liquibaseBundle.getAllowedPackages()) {
+ Package pkg = clazz.getPackage();
+ if (pkg != null) {
+ String pkgName = pkg.getName();
+ if (allowedPackage.equals(pkgName) || allowedPackage.startsWith(pkgName + ".")) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/liquibase-core/src/test/groovy/liquibase/change/core/AddNotNullConstraintChangeTest.groovy b/liquibase-core/src/test/groovy/liquibase/change/core/AddNotNullConstraintChangeTest.groovy
index 69289e5573a..411389c361d 100644
--- a/liquibase-core/src/test/groovy/liquibase/change/core/AddNotNullConstraintChangeTest.groovy
+++ b/liquibase-core/src/test/groovy/liquibase/change/core/AddNotNullConstraintChangeTest.groovy
@@ -1,6 +1,9 @@
package liquibase.change.core
import liquibase.change.StandardChangeTest
+import liquibase.database.core.MySQLDatabase
+import liquibase.statement.core.SetNullableStatement
+import liquibase.statement.core.UpdateStatement
public class AddNotNullConstraintChangeTest extends StandardChangeTest {
@@ -28,4 +31,80 @@ public class AddNotNullConstraintChangeTest extends StandardChangeTest {
reverses[0].getTableName() == "TABLE_NAME"
reverses[0].getColumnName() == "COL_HERE"
}
-}
\ No newline at end of file
+
+ def should_generateStatements_add_update_statement_before_not_null_constraint() {
+ given:
+ def change = new AddNotNullConstraintChange()
+ change.setTableName("table_name")
+ change.setColumnName("column_name")
+ change.setColumnDataType("varchar(20)")
+ change.setDefaultNullValue("Hello World!")
+
+ def database = new MySQLDatabase()
+
+ when:
+ def output = change.generateStatements(database)
+
+ then:
+ output.length == 2
+ output[0] instanceof UpdateStatement
+ def update = (UpdateStatement) output[0]
+ update.getTableName() == "table_name"
+ update.getNewColumnValues().size() == 1
+ update.getNewColumnValues().get("column_name") == "Hello World!"
+ update.getWhereClause() == "column_name IS NULL"
+
+ output[1] instanceof SetNullableStatement
+ }
+
+ def should_generateStatements_update_statement_handle_boolean_type() {
+ given:
+ def change = new AddNotNullConstraintChange()
+ change.setTableName("FOO")
+ change.setColumnName("BAR")
+ change.setColumnDataType("BOOLEAN")
+ change.setDefaultNullValue("false")
+
+ def database = new MySQLDatabase()
+
+ when:
+ def output = change.generateStatements(database)
+
+ then:
+ output.length == 2
+ output[0] instanceof UpdateStatement
+ def update = (UpdateStatement) output[0]
+ update.getTableName() == "FOO"
+ update.getNewColumnValues().size() == 1
+ update.getNewColumnValues().get("BAR") == 0
+ update.getWhereClause() == "BAR IS NULL"
+
+ output[1] instanceof SetNullableStatement
+ }
+
+ def should_generateStatements_update_statement_handle_bit_1_type() {
+ given:
+ def change = new AddNotNullConstraintChange()
+ change.setTableName("xxx")
+ change.setColumnName("col_name")
+ change.setColumnDataType("BIT(1)")
+ change.setDefaultNullValue("1")
+
+ def database = new MySQLDatabase()
+
+ when:
+ def output = change.generateStatements(database)
+
+ then:
+ output.length == 2
+ output[0] instanceof UpdateStatement
+ def update = (UpdateStatement) output[0]
+ update.getTableName() == "xxx"
+ update.getNewColumnValues().size() == 1
+ update.getNewColumnValues().get("col_name") == 1
+ update.getWhereClause() == "col_name IS NULL"
+
+ output[1] instanceof SetNullableStatement
+ }
+
+}