diff --git a/README.md b/README.md index c04b0dc3..b08f320a 100644 --- a/README.md +++ b/README.md @@ -805,6 +805,107 @@ you can fix it by running the following command where your driver is located: ``` zip -d google-cloud-spanner-jdbc-2.6.2-single-jar-with-dependencies.jar 'META-INF/.SF' 'META-INF/.RSA' 'META-INF/*SF' ``` +## Rosetta Extensions Feature +### Overview +The Extensions feature in Rosetta allows users to append custom SQL code to the Data Definition Language (DDL) operations generated during the execution of the apply command. This feature enhances the flexibility and functionality of Rosetta by enabling custom SQL commands to be executed at various stages of the DDL lifecycle. + +### Supported Extensions + + +Currently, Rosetta supports the following extension: +- DDL_EXTENSION: This extension type allows the addition of custom SQL commands to the DDL operations. + + +### DDL_EXTENSION Commands + + +Users can specify custom SQL commands to be executed at different points in the DDL lifecycle using the following commands: +- PRE_CREATE: Execute SQL before a CREATE operation. +- PRE_ALTER: Execute SQL before an ALTER operation. +- PRE_DROP: Execute SQL before a DROP operation. +- POST_CREATE: Execute SQL after a CREATE operation. +- POST_ALTER: Execute SQL after an ALTER operation. +- POST_DROP: Execute SQL after a DROP operation. + +### Example Usage + +Below is an example of how to use the DDL_EXTENSION to append custom SQL code to a table creation operation in the model.yaml file. +```yaml +- name: "fullname" + typeName: "varchar" + ordinalPosition: 0 + primaryKeySequenceId: 0 + columnDisplaySize: 2147483647 + scale: 0 + precision: 2147483647 + columnProperties: [] + primaryKey: false + nullable: true + autoincrement: false + extensions: + - name: SQL + type: DDL_EXTENSION + actions: + POST_CREATE: UPDATE basic_library.authors SET fullname = CONCAT(name, ' ', surname) WHERE fullname IS NULL; +``` +### How to Use +1. Define the Extension in model.yaml: +- Add the extensions section under the column or table definition where the custom SQL needs to be applied. +- Specify the name as SQL and the type as DDL_EXTENSION. +3. Specify Actions: +- Under actions, define the SQL commands to be executed for the desired DDL operations (e.g., POST_CREATE, PRE_ALTER). +### Example Scenarios + + +#### Scenario 1: Custom SQL After Table Creation + +If you need to populate or update data in a newly created table immediately after its creation, you can use the POST_CREATE command: +```yaml +- name: "fullname" + typeName: "varchar" + ordinalPosition: 0 + primaryKeySequenceId: 0 + columnDisplaySize: 2147483647 + scale: 0 + precision: 2147483647 + columnProperties: [] + primaryKey: false + nullable: true + autoincrement: false + extensions: + - name: SQL + type: DDL_EXTENSION + actions: + POST_CREATE: UPDATE basic_library.authors SET fullname = CONCAT(name, ' ', surname) WHERE fullname IS NULL; +``` +#### Scenario 2: Custom SQL Before Table Alteration +To execute custom SQL before altering a table, use the PRE_ALTER command: +```yaml +- name: "fullname" + typeName: "varchar" + ordinalPosition: 0 + primaryKeySequenceId: 0 + columnDisplaySize: 2147483647 + scale: 0 + precision: 2147483647 + columnProperties: [] + primaryKey: false + nullable: true + autoincrement: false + extensions: + - name: SQL + type: DDL_EXTENSION + actions: + PRE_ALTER: CREATE TEMPORARY TABLE temp_authors AS SELECT * FROM authors; +``` +### Best Practices + +- Validation: Ensure that the custom SQL commands are valid and tested to avoid errors during the apply command execution. +- Performance: Consider the performance implications of the custom SQL, especially for large datasets. +- Security: Validate and sanitize custom SQL to prevent SQL injection and other security vulnerabilities. +### Conclusion + +The Extensions feature in Rosetta, particularly the DDL_EXTENSION, provides a powerful way to customize the DDL lifecycle with user-defined SQL commands. By leveraging this feature, users can enhance the functionality and control of their data operations, ensuring that specific actions are performed at key stages of the DDL execution process. ## Copyright and License Information Unless otherwise specified, all content, including all source code files and documentation files in this repository are: diff --git a/common/src/main/java/com/adaptivescale/rosetta/common/models/Column.java b/common/src/main/java/com/adaptivescale/rosetta/common/models/Column.java index f77d9f4b..2d3d2f70 100644 --- a/common/src/main/java/com/adaptivescale/rosetta/common/models/Column.java +++ b/common/src/main/java/com/adaptivescale/rosetta/common/models/Column.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.Set; public class Column { @@ -24,6 +25,7 @@ public class Column { private List columnProperties = new ArrayList<>(); private Tests tests; private List foreignKeys; + private Set extensions; public Column() { } @@ -162,4 +164,12 @@ public Tests getTests() { public void setTests(Tests tests) { this.tests = tests; } + + public Set getExtensions() { + return extensions; + } + + public void setExtensions(Set extensions) { + this.extensions = extensions; + } } diff --git a/common/src/main/java/com/adaptivescale/rosetta/common/models/Extension.java b/common/src/main/java/com/adaptivescale/rosetta/common/models/Extension.java new file mode 100644 index 00000000..8dbacb7b --- /dev/null +++ b/common/src/main/java/com/adaptivescale/rosetta/common/models/Extension.java @@ -0,0 +1,36 @@ +package com.adaptivescale.rosetta.common.models; + + +import com.adaptivescale.rosetta.common.models.enums.ExtensionTypesEnum; + +import java.util.Map; + +public class Extension { + private String name; + private ExtensionTypesEnum type; + private Map actions; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ExtensionTypesEnum getType() { + return type; + } + + public void setType(ExtensionTypesEnum type) { + this.type = type; + } + + public Map getActions() { + return actions; + } + + public void setActions(Map actions) { + this.actions = actions; + } +} diff --git a/common/src/main/java/com/adaptivescale/rosetta/common/models/Table.java b/common/src/main/java/com/adaptivescale/rosetta/common/models/Table.java index 2969088f..e07f35b7 100644 --- a/common/src/main/java/com/adaptivescale/rosetta/common/models/Table.java +++ b/common/src/main/java/com/adaptivescale/rosetta/common/models/Table.java @@ -1,8 +1,6 @@ package com.adaptivescale.rosetta.common.models; -import java.util.Collection; -import java.util.List; -import java.util.Objects; +import java.util.*; public class Table { @@ -17,6 +15,8 @@ public class Table { private Collection columns; + private Set extensions; + public String getName() { return name; } @@ -74,7 +74,15 @@ public void setIndices(List indices) { this.indices = indices; } - @Override + public Set getExtensions() { + return extensions; + } + + public void setExtensions(Set extensions) { + this.extensions = extensions; + } + + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; diff --git a/common/src/main/java/com/adaptivescale/rosetta/common/models/enums/ExtensionTypesEnum.java b/common/src/main/java/com/adaptivescale/rosetta/common/models/enums/ExtensionTypesEnum.java new file mode 100644 index 00000000..bc880453 --- /dev/null +++ b/common/src/main/java/com/adaptivescale/rosetta/common/models/enums/ExtensionTypesEnum.java @@ -0,0 +1,5 @@ +package com.adaptivescale.rosetta.common.models.enums; + +public enum ExtensionTypesEnum { + DDL_EXTENSION, +} diff --git a/common/src/main/java/com/adaptivescale/rosetta/common/models/enums/OperationTypeEnum.java b/common/src/main/java/com/adaptivescale/rosetta/common/models/enums/OperationTypeEnum.java new file mode 100644 index 00000000..9ae66406 --- /dev/null +++ b/common/src/main/java/com/adaptivescale/rosetta/common/models/enums/OperationTypeEnum.java @@ -0,0 +1,10 @@ +package com.adaptivescale.rosetta.common.models.enums; + +public enum OperationTypeEnum { + PRE_CREATE, + PRE_ALTER, + PRE_DROP, + POST_CREATE, + POST_ALTER, + POST_DROP +} diff --git a/common/src/main/java/com/adaptivescale/rosetta/common/types/RosettaModuleTypes.java b/common/src/main/java/com/adaptivescale/rosetta/common/types/RosettaModuleTypes.java index 48489e53..b6dae4e6 100644 --- a/common/src/main/java/com/adaptivescale/rosetta/common/types/RosettaModuleTypes.java +++ b/common/src/main/java/com/adaptivescale/rosetta/common/types/RosettaModuleTypes.java @@ -25,4 +25,6 @@ public enum RosettaModuleTypes { TABLE_EXTRACTOR, VIEW_EXTRACTOR, DIFF_TESTER, + DDL_EXTENSION_TABLE, + DDL_EXTENSION_COLUMN, } diff --git a/ddl/src/main/java/com/adaptivescale/rosetta/ddl/DDLExtensionColumn.java b/ddl/src/main/java/com/adaptivescale/rosetta/ddl/DDLExtensionColumn.java new file mode 100644 index 00000000..249c7103 --- /dev/null +++ b/ddl/src/main/java/com/adaptivescale/rosetta/ddl/DDLExtensionColumn.java @@ -0,0 +1,17 @@ +package com.adaptivescale.rosetta.ddl; + +import com.adaptivescale.rosetta.common.models.Column; + + +public interface DDLExtensionColumn { + + String preCreateColumn(Column column, Object action); + String postCreateColumn(Column column, Object action); + + String preDropColumn(Column column, Object action); + String postDropColumn(Column column, Object action); + + String preAlterColumn(Column column, Object action); + String postAlterColumn(Column column, Object action); + +} diff --git a/ddl/src/main/java/com/adaptivescale/rosetta/ddl/DDLExtensionTable.java b/ddl/src/main/java/com/adaptivescale/rosetta/ddl/DDLExtensionTable.java new file mode 100644 index 00000000..8d0cd600 --- /dev/null +++ b/ddl/src/main/java/com/adaptivescale/rosetta/ddl/DDLExtensionTable.java @@ -0,0 +1,19 @@ +package com.adaptivescale.rosetta.ddl; + +import com.adaptivescale.rosetta.common.models.*; +import com.adaptivescale.rosetta.ddl.change.model.ColumnChange; +import com.adaptivescale.rosetta.ddl.change.model.ForeignKeyChange; + + +public interface DDLExtensionTable { + + String preCreateTable(Table table, Object action); + String postCreateTable(Table table, Object action); + + String preDropTable(Table actual, Object action); + String postDropTable(Table actual, Object action); + + String preAlterTable(Table expected, Object action); + String postAlterTable(Table expected, Object action); + +} diff --git a/ddl/src/main/java/com/adaptivescale/rosetta/ddl/change/ChangeHandlerImplementation.java b/ddl/src/main/java/com/adaptivescale/rosetta/ddl/change/ChangeHandlerImplementation.java index 70145717..81a39ce5 100644 --- a/ddl/src/main/java/com/adaptivescale/rosetta/ddl/change/ChangeHandlerImplementation.java +++ b/ddl/src/main/java/com/adaptivescale/rosetta/ddl/change/ChangeHandlerImplementation.java @@ -1,12 +1,21 @@ package com.adaptivescale.rosetta.ddl.change; +import com.adaptivescale.rosetta.common.helpers.ModuleLoader; +import com.adaptivescale.rosetta.common.models.Column; +import com.adaptivescale.rosetta.common.models.Extension; +import com.adaptivescale.rosetta.common.models.Table; +import com.adaptivescale.rosetta.common.models.enums.OperationTypeEnum; +import com.adaptivescale.rosetta.common.types.RosettaModuleTypes; import com.adaptivescale.rosetta.ddl.DDL; +import com.adaptivescale.rosetta.ddl.DDLExtensionColumn; +import com.adaptivescale.rosetta.ddl.DDLExtensionTable; import com.adaptivescale.rosetta.ddl.change.model.*; import lombok.extern.slf4j.Slf4j; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +import static com.adaptivescale.rosetta.common.models.enums.OperationTypeEnum.*; @Slf4j public class ChangeHandlerImplementation implements ChangeHandler { @@ -21,7 +30,7 @@ public ChangeHandlerImplementation(DDL ddl, Comparator> changeComparat @Override public String createDDLForChanges(List> changes) { - if(changeComparator != null){ + if (changeComparator != null) { changes.sort(changeComparator); } @@ -69,11 +78,53 @@ public String onDatabaseChange(DatabaseChange databaseChange) { public String onTableChange(TableChange change) { switch (change.getStatus()) { case DROP: - return ddl.dropTable(change.getActual()); + StringBuilder dropQueryBuilder = new StringBuilder(); + if (change.getExpected() != null && change.getExpected().getExtensions() != null) { + Optional first = change.getExpected().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(PRE_DROP.name())).findFirst(); + first.ifPresent(extension -> dropQueryBuilder.append(executeTableExtensions(change.getExpected(), PRE_DROP, extension.getActions().get(PRE_DROP.name())))); + } + + dropQueryBuilder.append(ddl.dropTable(change.getActual())); + + if (change.getExpected() != null && change.getExpected().getExtensions() != null) { + Optional first = change.getExpected().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(POST_DROP.name())).findFirst(); + first.ifPresent(extension -> dropQueryBuilder.append(executeTableExtensions(change.getExpected(), POST_DROP, extension.getActions().get(POST_DROP.name())))); + } + return dropQueryBuilder.toString(); case ADD: - return ddl.createTable(change.getExpected(), false); + StringBuilder addQueryBuilder = new StringBuilder(); + if (change.getExpected() != null && change.getExpected().getExtensions() != null) { + Optional first = change.getExpected().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(PRE_CREATE.name())).findFirst(); + first.ifPresent(extension -> addQueryBuilder.append(executeTableExtensions(change.getExpected(), PRE_CREATE, extension.getActions().get(PRE_CREATE.name())))); + } + + addQueryBuilder.append(ddl.createTable(change.getExpected(), false)); + + if (change.getExpected() != null && change.getExpected().getExtensions() != null) { + Optional first = change.getExpected().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(POST_CREATE.name())).findFirst(); + first.ifPresent(extension -> addQueryBuilder.append(executeTableExtensions(change.getExpected(), POST_CREATE, extension.getActions().get(POST_CREATE.name())))); + } + return addQueryBuilder.toString(); case ALTER: - return ddl.alterTable(change.getExpected(), change.getActual()); + StringBuilder alterQueryBuilder = new StringBuilder(); + if (change.getExpected() != null && change.getExpected().getExtensions() != null) { + Optional first = change.getExpected().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(PRE_ALTER.name())).findFirst(); + first.ifPresent(extension -> alterQueryBuilder.append(executeTableExtensions(change.getExpected(), PRE_ALTER, extension.getActions().get(PRE_ALTER.name())))); + } + + alterQueryBuilder.append(ddl.alterTable(change.getExpected(), change.getActual())); + + if (change.getExpected() != null && change.getExpected().getExtensions() != null) { + Optional first = change.getExpected().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(POST_ALTER.name())).findFirst(); + first.ifPresent(extension -> alterQueryBuilder.append(executeTableExtensions(change.getExpected(), POST_ALTER, extension.getActions().get(POST_ALTER.name())))); + } + return alterQueryBuilder.toString(); default: throw new RuntimeException("Operation " + change.getStatus() + " for table not supported"); } @@ -93,16 +144,123 @@ public String onTableSchemaChange(TableSchemaChange change) { public String onColumnChange(ColumnChange change) { switch (change.getStatus()) { case ALTER: - return ddl.alterColumn(change); + StringBuilder alterQueryBuilder = new StringBuilder(); + if (change.getExpected() != null && change.getExpected().getExtensions() != null) { + Optional first = change.getExpected().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(PRE_ALTER.name())).findFirst(); + first.ifPresent(extension -> alterQueryBuilder.append(executeColumnExtensions(change.getExpected(), PRE_ALTER, extension.getActions().get(PRE_ALTER.name())))); + } + + alterQueryBuilder.append(ddl.alterColumn(change)); + + if (change.getExpected() != null && change.getExpected().getExtensions() != null) { + Optional first = change.getExpected().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(POST_ALTER.name())).findFirst(); + first.ifPresent(extension -> alterQueryBuilder.append(executeColumnExtensions(change.getExpected(), POST_ALTER, extension.getActions().get(POST_ALTER.name())))); + } + return alterQueryBuilder.toString(); case DROP: - return ddl.dropColumn(change); + StringBuilder dropQueryBuilder = new StringBuilder(); + if (change.getActual() != null && change.getActual().getExtensions() != null) { + Optional first = change.getActual().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(PRE_DROP.name())).findFirst(); + first.ifPresent(extension -> dropQueryBuilder.append(executeColumnExtensions(change.getActual(), PRE_DROP, extension.getActions().get(PRE_DROP.name())))); + } + dropQueryBuilder.append(ddl.dropColumn(change)); + if (change.getActual() != null && change.getActual().getExtensions() != null) { + Optional first = change.getActual().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(POST_DROP.name())).findFirst(); + first.ifPresent(extension -> dropQueryBuilder.append(executeColumnExtensions(change.getActual(), POST_DROP, extension.getActions().get(POST_DROP.name())))); + } + return dropQueryBuilder.toString(); case ADD: - return ddl.addColumn(change); + StringBuilder addQueryBuilder = new StringBuilder(); + if (change.getExpected() != null && change.getExpected().getExtensions() != null) { + Optional first = change.getExpected().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(PRE_CREATE.name())).findFirst(); + first.ifPresent(extension -> addQueryBuilder.append(executeColumnExtensions(change.getExpected(), PRE_CREATE, extension.getActions().get(PRE_CREATE.name())))); + } + addQueryBuilder.append(ddl.addColumn(change)); + + if (change.getExpected() != null && change.getExpected().getExtensions() != null) { + Optional first = change.getExpected().getExtensions().stream() + .filter(extension -> extension.getActions().containsKey(POST_CREATE.name())).findFirst(); + first.ifPresent(extension -> addQueryBuilder.append(executeColumnExtensions(change.getExpected(), POST_CREATE, extension.getActions().get(POST_CREATE.name())))); + } + return addQueryBuilder.toString(); default: throw new RuntimeException("Operation " + change.getStatus() + " for column not supported"); } } + public String executeTableExtensions(Table table, OperationTypeEnum status, Object action) { + Optional> tableExtension = ModuleLoader.loadModuleByAnnotationClassValues(String.format("%s.%s",DDLExtensionTable.class.getPackageName(),"extensions.table") , RosettaModuleTypes.DDL_EXTENSION_TABLE, "SQL"); + DDLExtensionTable ddlExtensionTable = null; + if (tableExtension.isPresent()) { + try { + ddlExtensionTable = (DDLExtensionTable) tableExtension.get().getDeclaredConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { + log.warn("Failed to load table ddl extension %s - skipping", tableExtension.get().getName()); + } + } else { + log.warn("No table ddl extension found - skipping", tableExtension.get().getName()); + } + if (ddlExtensionTable == null) { + return ""; + } + switch (status) { + case PRE_CREATE: + return ddlExtensionTable.preCreateTable(table, action); + case PRE_ALTER: + return ddlExtensionTable.preAlterTable(table, action); + case PRE_DROP: + return ddlExtensionTable.preDropTable(table, action); + case POST_CREATE: + return ddlExtensionTable.postCreateTable(table, action); + case POST_ALTER: + return ddlExtensionTable.postAlterTable(table, action); + case POST_DROP: + return ddlExtensionTable.postDropTable(table, action); + default: + return ""; + } + } + + public String executeColumnExtensions(Column column, OperationTypeEnum status, Object action) { + Optional> columnExtension = ModuleLoader.loadModuleByAnnotationClassValues(String.format("%s.%s",DDLExtensionColumn.class.getPackageName(),"extensions.column"), RosettaModuleTypes.DDL_EXTENSION_COLUMN, "SQL"); + DDLExtensionColumn ddlExtensionColumn = null; + if (columnExtension.isPresent()) { + try { + ddlExtensionColumn = (DDLExtensionColumn) columnExtension.get().getDeclaredConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { + log.warn("Failed to load column ddl extension %s - skipping", columnExtension.get().getName()); + } + } else { + log.warn("No column ddl extension found - skipping", columnExtension.get().getName()); + } + if (ddlExtensionColumn == null) { + return ""; + } + switch (status) { + case PRE_CREATE: + return ddlExtensionColumn.preCreateColumn(column, action); + case PRE_ALTER: + return ddlExtensionColumn.preAlterColumn(column, action); + case PRE_DROP: + return ddlExtensionColumn.preDropColumn(column, action); + case POST_CREATE: + return ddlExtensionColumn.postCreateColumn(column, action); + case POST_ALTER: + return ddlExtensionColumn.postAlterColumn(column, action); + case POST_DROP: + return ddlExtensionColumn.postDropColumn(column, action); + default: + return ""; + } + } + @Override public String onForeignKeyChange(ForeignKeyChange change) { switch (change.getStatus()) { @@ -142,4 +300,4 @@ public String onViewChange(ViewChange change) { throw new RuntimeException("Operation " + change.getStatus() + " for view not supported"); } } -} +} \ No newline at end of file diff --git a/ddl/src/main/java/com/adaptivescale/rosetta/ddl/extensions/column/DDLColumnChangeTest.java b/ddl/src/main/java/com/adaptivescale/rosetta/ddl/extensions/column/DDLColumnChangeTest.java new file mode 100644 index 00000000..f2b68218 --- /dev/null +++ b/ddl/src/main/java/com/adaptivescale/rosetta/ddl/extensions/column/DDLColumnChangeTest.java @@ -0,0 +1,42 @@ +package com.adaptivescale.rosetta.ddl.extensions.column; + +import com.adaptivescale.rosetta.common.annotations.RosettaModule; +import com.adaptivescale.rosetta.common.models.Column; +import com.adaptivescale.rosetta.common.types.RosettaModuleTypes; +import com.adaptivescale.rosetta.ddl.DDLExtensionColumn; + +@RosettaModule( + name = "SQL", + type = RosettaModuleTypes.DDL_EXTENSION_COLUMN +) +public class DDLColumnChangeTest implements DDLExtensionColumn { + @Override + public String preCreateColumn(Column column, Object action) { + return action.toString(); + } + + @Override + public String postCreateColumn(Column column, Object action) { + return action.toString(); + } + + @Override + public String preDropColumn(Column column, Object action) { + return action.toString(); + } + + @Override + public String postDropColumn(Column column, Object action) { + return action.toString(); + } + + @Override + public String preAlterColumn(Column column, Object action) { + return action.toString(); + } + + @Override + public String postAlterColumn(Column column, Object action) { + return action.toString(); + } +} diff --git a/ddl/src/main/java/com/adaptivescale/rosetta/ddl/extensions/table/DDLTableChangeTest.java b/ddl/src/main/java/com/adaptivescale/rosetta/ddl/extensions/table/DDLTableChangeTest.java new file mode 100644 index 00000000..32f5e187 --- /dev/null +++ b/ddl/src/main/java/com/adaptivescale/rosetta/ddl/extensions/table/DDLTableChangeTest.java @@ -0,0 +1,43 @@ +package com.adaptivescale.rosetta.ddl.extensions.table; + +import com.adaptivescale.rosetta.common.annotations.RosettaModule; +import com.adaptivescale.rosetta.common.models.Table; +import com.adaptivescale.rosetta.common.types.RosettaModuleTypes; +import com.adaptivescale.rosetta.ddl.DDLExtensionTable; + +@RosettaModule( + name = "SQL", + type = RosettaModuleTypes.DDL_EXTENSION_TABLE +) +public class DDLTableChangeTest implements DDLExtensionTable { + + @Override + public String preCreateTable(Table table, Object action) { + return action.toString(); + } + + @Override + public String postCreateTable(Table table, Object action) { + return action.toString(); + } + + @Override + public String preDropTable(Table actual, Object action) { + return action.toString(); + } + + @Override + public String postDropTable(Table actual, Object action) { + return action.toString(); + } + + @Override + public String preAlterTable(Table expected, Object action) { + return action.toString(); + } + + @Override + public String postAlterTable(Table expected, Object action) { + return action.toString(); + } +}