Skip to content

Commit 8d3cae5

Browse files
author
Timothy Rundle
committed
Add enum support for H2 / MySql to ColumnSnapshotGenerator
1 parent ba88b2e commit 8d3cae5

File tree

2 files changed

+55
-30
lines changed

2 files changed

+55
-30
lines changed

src/main/java/liquibase/ext/hibernate/snapshot/ColumnSnapshotGenerator.java

+48-30
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package liquibase.ext.hibernate.snapshot;
22

3-
import java.util.Iterator;
43
import java.util.List;
4+
import java.util.Optional;
55
import java.util.Properties;
66
import java.util.regex.Matcher;
77
import java.util.regex.Pattern;
@@ -11,6 +11,7 @@
1111
import org.hibernate.dialect.PostgreSQLDialect;
1212
import org.hibernate.id.ExportableColumn;
1313
import org.hibernate.mapping.SimpleValue;
14+
import org.hibernate.type.SqlTypes;
1415

1516
import liquibase.Scope;
1617
import liquibase.datatype.DataTypeFactory;
@@ -28,8 +29,9 @@
2829
import liquibase.util.SqlUtil;
2930
import liquibase.util.StringUtil;
3031

32+
3133
/**
32-
* Columns are snapshotted along with with Tables in {@link TableSnapshotGenerator} but this class needs to be here to keep the default ColumnSnapshotGenerator from running.
34+
* Columns are snapshotted along with Tables in {@link TableSnapshotGenerator} but this class needs to be here to keep the default ColumnSnapshotGenerator from running.
3335
* Ideally the column logic would be moved out of the TableSnapshotGenerator to better work in situations where the object types to snapshot are being controlled, but that is not the case yet.
3436
*/
3537
public class ColumnSnapshotGenerator extends HibernateSnapshotGenerator {
@@ -102,7 +104,8 @@ protected void snapshotColumn(Column column, DatabaseSnapshot snapshot) throws D
102104
if (hibernateColumn.getName().equalsIgnoreCase(column.getName())) {
103105

104106
String defaultValue = null;
105-
String hibernateType = hibernateColumn.getSqlType(metadata.getTypeConfiguration(), dialect, metadata);
107+
String hibernateType = hibernateColumn.getSqlType(metadata);
108+
106109
Matcher defaultValueMatcher = Pattern.compile("(?i) DEFAULT\\s+(.*)").matcher(hibernateType);
107110
if (defaultValueMatcher.find()) {
108111
defaultValue = defaultValueMatcher.group(1);
@@ -118,7 +121,15 @@ protected void snapshotColumn(Column column, DatabaseSnapshot snapshot) throws D
118121
Scope.getCurrentScope().getLog(getClass()).info("Found column " + column.getName() + " " + column.getType().toString());
119122

120123
column.setRemarks(hibernateColumn.getComment());
121-
if (hibernateColumn.getValue() instanceof SimpleValue) {
124+
125+
// DataTypeFactory.from and SqlUtil.parseValue rely on the database type however,
126+
// the liquibase-core does not know about the fake hibernate database so not all conditions
127+
// are handled correctly for enums.
128+
boolean isEnumType = Optional.ofNullable(dataType.getDataTypeId())
129+
.map(SqlTypes::isEnumType)
130+
.orElse(false);
131+
132+
if (!isEnumType && hibernateColumn.getValue() instanceof SimpleValue) {
122133
DataType parseType;
123134
if (DataTypeFactory.getInstance().from(dataType, database) instanceof UnknownType) {
124135
parseType = new DataType(((SimpleValue) hibernateColumn.getValue()).getTypeName());
@@ -181,46 +192,53 @@ protected void snapshotColumn(Column column, DatabaseSnapshot snapshot) throws D
181192
}
182193
}
183194

184-
protected DataType toDataType(String hibernateType, Integer sqlTypeCode) throws DatabaseException {
195+
protected DataType toDataType(String hibernateType, Integer sqlTypeCode) {
185196
Matcher matcher = pattern.matcher(hibernateType);
186197
if (!matcher.matches()) {
187198
return null;
188199
}
189200

190-
String typeName = matcher.group(1);
201+
DataType dataType;
191202

192-
// Liquibase seems to use 'with timezone' instead of 'with time zone',
193-
// so we remove any 'with time zone' suffixes here.
194-
// The corresponding 'with timezone' suffix will then be added below,
195-
// because in that case hibernateType also ends with 'with time zone'.
196-
if (typeName.toLowerCase().endsWith(SQL_TIMEZONE_SUFFIX)) {
197-
typeName = typeName.substring(0, typeName.length() - SQL_TIMEZONE_SUFFIX.length()).stripTrailing();
198-
}
203+
// Small hack for enums until DataType adds support for them
204+
if (Optional.ofNullable(sqlTypeCode).map(SqlTypes::isEnumType).orElse(false)) {
205+
dataType = new DataType(hibernateType);
206+
} else {
207+
String typeName = matcher.group(1);
208+
209+
// Liquibase seems to use 'with timezone' instead of 'with time zone',
210+
// so we remove any 'with time zone' suffixes here.
211+
// The corresponding 'with timezone' suffix will then be added below,
212+
// because in that case hibernateType also ends with 'with time zone'.
213+
if (typeName.toLowerCase().endsWith(SQL_TIMEZONE_SUFFIX)) {
214+
typeName = typeName.substring(0, typeName.length() - SQL_TIMEZONE_SUFFIX.length()).stripTrailing();
215+
}
199216

200-
// If hibernateType ends with 'with time zone' we need to add the corresponding
201-
// 'with timezone' suffix to the Liquibase type.
202-
if (hibernateType.toLowerCase().endsWith(SQL_TIMEZONE_SUFFIX)) {
203-
typeName += (" " + LIQUIBASE_TIMEZONE_SUFFIX);
204-
}
217+
// If hibernateType ends with 'with time zone' we need to add the corresponding
218+
// 'with timezone' suffix to the Liquibase type.
219+
if (hibernateType.toLowerCase().endsWith(SQL_TIMEZONE_SUFFIX)) {
220+
typeName += (" " + LIQUIBASE_TIMEZONE_SUFFIX);
221+
}
205222

206-
DataType dataType = new DataType(typeName);
207-
if (matcher.group(3).isEmpty()) {
208-
if (!matcher.group(2).isEmpty()) {
223+
dataType = new DataType(typeName);
224+
if (matcher.group(3).isEmpty()) {
225+
if (!matcher.group(2).isEmpty()) {
226+
dataType.setColumnSize(Integer.parseInt(matcher.group(2)));
227+
}
228+
} else {
209229
dataType.setColumnSize(Integer.parseInt(matcher.group(2)));
230+
dataType.setDecimalDigits(Integer.parseInt(matcher.group(3)));
210231
}
211-
} else {
212-
dataType.setColumnSize(Integer.parseInt(matcher.group(2)));
213-
dataType.setDecimalDigits(Integer.parseInt(matcher.group(3)));
214-
}
215232

216-
String extra = StringUtil.trimToNull(matcher.group(4));
217-
if (extra != null) {
218-
if (extra.equalsIgnoreCase("char")) {
219-
dataType.setColumnSizeUnit(DataType.ColumnSizeUnit.CHAR);
233+
String extra = StringUtil.trimToNull(matcher.group(4));
234+
if (extra != null) {
235+
if (extra.equalsIgnoreCase("char")) {
236+
dataType.setColumnSizeUnit(DataType.ColumnSizeUnit.CHAR);
237+
}
220238
}
221239
}
222240

223-
Scope.getCurrentScope().getLog(getClass()).info("Converted column data type - hibernate type: " + hibernateType + ", SQL type: " + sqlTypeCode + ", type name: " + typeName);
241+
Scope.getCurrentScope().getLog(getClass()).info("Converted column data type - hibernate type: " + hibernateType + ", SQL type: " + sqlTypeCode + ", type name: " + dataType.getTypeName());
224242

225243
dataType.setDataTypeId(sqlTypeCode);
226244
return dataType;

src/test/java/liquibase/ext/hibernate/snapshot/ColumnSnapshotGeneratorTest.java

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import liquibase.exception.DatabaseException;
44
import liquibase.structure.core.DataType;
5+
import org.hibernate.type.SqlTypes;
56
import org.junit.Test;
67

78
import java.sql.Types;
@@ -27,5 +28,11 @@ public void toDataType() throws DatabaseException {
2728
assertEquals(30, varcharChar.getColumnSize().intValue());
2829
assertEquals(DataType.ColumnSizeUnit.CHAR, varcharChar.getColumnSizeUnit());
2930

31+
32+
DataType enumType = columnSnapshotGenerator.toDataType("enum ('a', 'b', 'c')", SqlTypes.ENUM);
33+
assertEquals("enum ('a', 'b', 'c')", enumType.getTypeName());
34+
assertNull(enumType.getColumnSize());
35+
assertEquals(SqlTypes.ENUM, enumType.getDataTypeId().intValue());
36+
assertNull(enumType.getColumnSizeUnit());
3037
}
3138
}

0 commit comments

Comments
 (0)