Merge branch 'release/5.45.0'
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / database / update / ColumnAdder.java
index eaa0c4edfe16247f53fd78844a88b99474240b5b..5fdf1e07a4c7b7c1d934589b951966c12dffaab2 100644 (file)
-// $Id$\r
-/**\r
-* Copyright (C) 2009 EDIT\r
-* European Distributed Institute of Taxonomy\r
-* http://www.e-taxonomy.eu\r
-*\r
-* The contents of this file are subject to the Mozilla Public License Version 1.1\r
-* See LICENSE.TXT at the top of this package for the full license terms.\r
-*/\r
-package eu.etaxonomy.cdm.database.update;\r
-\r
-import java.sql.Types;\r
-\r
-import org.apache.log4j.Logger;\r
-\r
-import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;\r
-import eu.etaxonomy.cdm.database.DatabaseTypeEnum;\r
-import eu.etaxonomy.cdm.database.ICdmDataSource;\r
-\r
-/**\r
- * @author a.mueller\r
- * @date 16.09.2010\r
- *\r
- */\r
-public class ColumnAdder extends AuditedSchemaUpdaterStepBase<ColumnAdder> implements ISchemaUpdaterStep {\r
-       private static final Logger logger = Logger.getLogger(ColumnAdder.class);\r
-\r
-       private final String newColumnName;\r
-       private final String columnType;\r
-       private final Object defaultValue;\r
-       private boolean isNotNull;\r
-\r
-       private final String referencedTable;\r
-\r
-       public static final ColumnAdder NewIntegerInstance(String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull, String referencedTable){\r
-               return new ColumnAdder(stepName, tableName, newColumnName, "int", includeAudTable, null, notNull, referencedTable);\r
-       }\r
-\r
-       public static final ColumnAdder NewIntegerInstance(String stepName, String tableName, String newColumnName, boolean includeAudTable, Integer defaultValue, boolean notNull){\r
-               return new ColumnAdder(stepName, tableName, newColumnName, "int", includeAudTable, defaultValue, notNull, null);\r
-       }\r
-\r
-       public static final ColumnAdder NewTinyIntegerInstance(String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){\r
-               return new ColumnAdder(stepName, tableName, newColumnName, "tinyint", includeAudTable, null, notNull, null);\r
-       }\r
-\r
-       public static final ColumnAdder NewDoubleInstance(String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){\r
-               return new ColumnAdder(stepName, tableName, newColumnName, "double", includeAudTable, null, notNull, null);\r
-       }\r
-\r
-       public static final ColumnAdder NewBooleanInstance(String stepName, String tableName, String newColumnName, boolean includeAudTable, Boolean defaultValue){\r
-               return new ColumnAdder(stepName, tableName, newColumnName, "bit", includeAudTable, defaultValue, false, null);\r
-       }\r
-\r
-       public static final ColumnAdder NewStringInstance(String stepName, String tableName, String newColumnName, boolean includeAudTable){\r
-               return new ColumnAdder(stepName, tableName, newColumnName, "nvarchar(255)", includeAudTable, null, false, null);\r
-       }\r
-\r
-       public static final ColumnAdder NewStringInstance(String stepName, String tableName, String newColumnName, int length, boolean includeAudTable){\r
-               return new ColumnAdder(stepName, tableName, newColumnName, "nvarchar("+length+")", includeAudTable, null, false, null);\r
-       }\r
-\r
-       public static final ColumnAdder NewClobInstance(String stepName, String tableName, String newColumnName, boolean includeAudTable){\r
-               return new ColumnAdder(stepName, tableName, newColumnName, "clob", includeAudTable, null, false, null);\r
-       }\r
-\r
-       public static final ColumnAdder NewDateTimeInstance(String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){\r
-               return new ColumnAdder(stepName, tableName, newColumnName, "datetime", includeAudTable, null, notNull, null);\r
-       }\r
-\r
-       protected ColumnAdder(String stepName, String tableName, String newColumnName, String columnType, boolean includeAudTable, Object defaultValue, boolean notNull, String referencedTable) {\r
-               super(stepName, tableName, includeAudTable);\r
-               this.newColumnName = newColumnName;\r
-               this.columnType = columnType;\r
-               this.defaultValue = defaultValue;\r
-               this.isNotNull = notNull;\r
-               this.referencedTable = referencedTable;\r
-       }\r
-\r
-       public ColumnAdder setNotNull(boolean isNotNull) {\r
-               this.isNotNull = isNotNull;\r
-               return this;\r
-       }\r
-\r
-       @Override\r
-       protected boolean invokeOnTable(String tableName, ICdmDataSource datasource, IProgressMonitor monitor, CaseType caseType) {\r
-               boolean result = true;\r
-               try {\r
-                       String updateQuery = getUpdateQueryString(tableName, datasource, monitor);\r
-                       datasource.executeUpdate(updateQuery);\r
-\r
-                       if (defaultValue instanceof Boolean){\r
-                               String defaultValueQuery = "UPDATE @tableName SET @columnName = " + (defaultValue == null ? "NULL" : getBoolean((Boolean) defaultValue, datasource));\r
-                               defaultValueQuery = defaultValueQuery.replace("@tableName", tableName);\r
-                               defaultValueQuery = defaultValueQuery.replace("@columnName", newColumnName);\r
-                               datasource.executeUpdate(defaultValueQuery);\r
-                       }else if (defaultValue instanceof Integer){\r
-                               String defaultValueQuery = "UPDATE @tableName SET @columnName = " + (defaultValue == null ? "NULL" : defaultValue);\r
-                               defaultValueQuery = defaultValueQuery.replace("@tableName", tableName);\r
-                               defaultValueQuery = defaultValueQuery.replace("@columnName", newColumnName);\r
-                               datasource.executeUpdate(defaultValueQuery);\r
-                       }else if (defaultValue != null){\r
-                               logger.warn("Default Value not implemented for type " + defaultValue.getClass().getName());\r
-                       }\r
-                       if (referencedTable != null){\r
-                               result &= TableCreator.makeForeignKey(tableName, datasource, monitor, newColumnName, referencedTable, caseType);\r
-                       }\r
-\r
-                       return result;\r
-               } catch ( Exception e) {\r
-                       monitor.warning(e.getMessage(), e);\r
-                       logger.error(e);\r
-                       return false;\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Returns the update query string. tableName must already be cased correctly. See {@link CaseType}.\r
-        * @param tableName correctly cased table name\r
-        * @param datasource data source\r
-        * @param monitor monitor\r
-        * @return the query string\r
-        * @throws DatabaseTypeNotSupportedException\r
-        */\r
-       public String getUpdateQueryString(String tableName, ICdmDataSource datasource, IProgressMonitor monitor) throws DatabaseTypeNotSupportedException {\r
-               String updateQuery;\r
-               DatabaseTypeEnum type = datasource.getDatabaseType();\r
-               String databaseColumnType = getDatabaseColumnType(datasource, this.columnType);\r
-\r
-               if (type.equals(DatabaseTypeEnum.SqlServer2005)){\r
-                       //MySQL allows both syntaxes\r
-                       updateQuery = "ALTER TABLE @tableName ADD @columnName @columnType";\r
-               }else if (type.equals(DatabaseTypeEnum.H2) || type.equals(DatabaseTypeEnum.PostgreSQL) || type.equals(DatabaseTypeEnum.MySQL)){\r
-                       updateQuery = "ALTER TABLE @tableName @addSeparator @columnName @columnType";\r
-               }else{\r
-                       updateQuery = null;\r
-                       String warning = "Update step '" + this.getStepName() + "' is not supported by " + type.getName();\r
-                       monitor.warning(warning);\r
-                       throw new DatabaseTypeNotSupportedException(warning);\r
-               }\r
-               if (isNotNull){\r
-                       updateQuery += " NOT NULL";\r
-               }\r
-               updateQuery = updateQuery.replace("@tableName", tableName);\r
-               updateQuery = updateQuery.replace("@columnName", newColumnName);\r
-               updateQuery = updateQuery.replace("@columnType", databaseColumnType);\r
-               updateQuery = updateQuery.replace("@addSeparator", getAddColumnSeperator(datasource));\r
-\r
-               return updateQuery;\r
-       }\r
-\r
-       protected static String getDatabaseColumnType(ICdmDataSource datasource, String columnType) {\r
-               String result = columnType;\r
-               DatabaseTypeEnum dbType = datasource.getDatabaseType();\r
-               //nvarchar\r
-               if (dbType.equals(DatabaseTypeEnum.PostgreSQL)){  //TODO use PostgeSQL82 Dialect infos\r
-                       result = result.replace("nvarchar", "varchar");\r
-                       result = result.replace("double", "float8");\r
-                       result = result.replace("bit", DatabaseTypeEnum.PostgreSQL.getHibernateDialect().getTypeName(Types.BIT));\r
-                       result = result.replace("datetime", DatabaseTypeEnum.PostgreSQL.getHibernateDialect().getTypeName(Types.TIMESTAMP));\r
-                       result = result.replace("tinyint", DatabaseTypeEnum.PostgreSQL.getHibernateDialect().getTypeName(Types.TINYINT));\r
-               }\r
-               //CLOB\r
-               if (columnType.equalsIgnoreCase("clob")){\r
-                       //TODO use hibernate dialects\r
-                       if (dbType.equals(DatabaseTypeEnum.MySQL)){\r
-                               result = "longtext";\r
-                       }else if (dbType.equals(DatabaseTypeEnum.H2)){\r
-                               result = "CLOB";  //or NVARCHAR\r
-                       }else if (dbType.equals(DatabaseTypeEnum.PostgreSQL)){\r
-                               result = "text";\r
-                       }else if (dbType.equals(DatabaseTypeEnum.SqlServer2005)){\r
-                               result = "NVARCHAR(MAX)";\r
-                       }\r
-               }\r
-               return result;\r
-       }\r
-\r
-\r
-       /**\r
-        * Returns the sql keywords for adding a column. This is usually 'ADD' or 'ADD COLUMN'\r
-        * @param datasource\r
-        * @return\r
-        * @throws DatabaseTypeNotSupportedException\r
-        */\r
-       public static String getAddColumnSeperator(ICdmDataSource datasource) throws DatabaseTypeNotSupportedException {\r
-               DatabaseTypeEnum type = datasource.getDatabaseType();\r
-               if (type.equals(DatabaseTypeEnum.SqlServer2005)){\r
-                       return "ADD ";\r
-               }else if (type.equals(DatabaseTypeEnum.H2) || type.equals(DatabaseTypeEnum.PostgreSQL) || type.equals(DatabaseTypeEnum.MySQL)){\r
-                       return "ADD COLUMN ";\r
-               }else{\r
-                       throw new DatabaseTypeNotSupportedException(datasource.getName());\r
-               }\r
-       }\r
-\r
-       public String getReferencedTable() {\r
-               return referencedTable;\r
-       }\r
-\r
-\r
-       public String getNewColumnName() {\r
-               return newColumnName;\r
-       }\r
-\r
-}\r
+/**
+* Copyright (C) 2009 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.cdm.database.update;
+
+import java.util.List;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
+import eu.etaxonomy.cdm.database.DatabaseTypeEnum;
+import eu.etaxonomy.cdm.database.ICdmDataSource;
+
+/**
+ * @author a.mueller
+ * @since 16.09.2010
+ */
+public class ColumnAdder extends AuditedSchemaUpdaterStepBase {
+
+    private static final Logger logger = LogManager.getLogger();
+
+       private final String newColumnName;
+       private final Datatype columnType;
+       private final Integer size;
+       private final Integer scale;
+       private final Object defaultValue;
+       private boolean isNotNull;
+       private final String referencedTable;
+
+       /**
+        * Add ForeignKey.
+        */
+       public static final ColumnAdder NewIntegerInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull, String referencedTable){
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.INTEGER, null, null, includeAudTable, null, notNull, referencedTable);
+       }
+
+       public static final ColumnAdder NewIntegerInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, Integer defaultValue, boolean notNull){
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.INTEGER, null, null, includeAudTable, defaultValue, notNull, null);
+       }
+
+       public static final ColumnAdder NewTinyIntegerInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.TINYINTEGER, null, null, includeAudTable, null, notNull, null);
+       }
+
+       public static final ColumnAdder NewDoubleInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.DOUBLE, null, null, includeAudTable, null, notNull, null);
+       }
+
+    public static final ColumnAdder NewDecimalInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, int precision,  int scale, boolean includeAudTable, Integer defaultValue, boolean notNull){
+        return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.BIGDECIMAL, precision, scale, includeAudTable, defaultValue, notNull, null);
+    }
+
+       public static final ColumnAdder NewBooleanInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, Boolean defaultValue){
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.BIT, null, null, includeAudTable, defaultValue, false, null);
+       }
+
+       /**
+        * Adds a string column with length 255 and default value <code>null</code>
+        */
+       public static final ColumnAdder NewStringInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable){
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, 255, null, includeAudTable, null, false, null);
+       }
+
+    public static final ColumnAdder NewDTYPEInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String defaultValue, boolean includeAudTable){
+        return new ColumnAdder(stepList, stepName, tableName, "DTYPE", Datatype.VARCHAR, 31, null, includeAudTable, defaultValue, true, null);
+    }
+
+    /**
+     * Adds a string column with the given length and default value <code>null</code>
+     */
+       public static final ColumnAdder NewStringInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, int length, boolean includeAudTable){
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, length, null, includeAudTable, null, false, null);
+       }
+
+    /**
+     * Adds a string column with the given length and the given default value
+     */
+    public static final ColumnAdder NewStringInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, int length, String defaultValue, boolean includeAudTable){
+        return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, length, null, includeAudTable, defaultValue, false, null);
+    }
+
+       public static final ColumnAdder NewClobInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable){
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.CLOB, null, null, includeAudTable, null, false, null);
+       }
+
+       public static final ColumnAdder NewDateTimeInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.DATETIME, null, null, includeAudTable, null, notNull, null);
+       }
+
+       protected ColumnAdder(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName,
+               Datatype columnType, Integer size, Integer scale, boolean includeAudTable, Object defaultValue, boolean notNull, String referencedTable) {
+               super(stepList, stepName, tableName, includeAudTable);
+               this.newColumnName = newColumnName;
+               this.columnType = columnType;
+               this.size = size;
+               this.defaultValue = defaultValue;
+               this.isNotNull = notNull;
+               this.referencedTable = referencedTable;
+               this.scale = scale;
+       }
+
+       public ColumnAdder setNotNull(boolean isNotNull) {
+               this.isNotNull = isNotNull;
+               return this;
+       }
+
+    @Override
+    protected void invokeOnTable(String tableName, ICdmDataSource datasource,
+            IProgressMonitor monitor, CaseType caseType, SchemaUpdateResult result) {
+
+        try {
+                       String updateQuery = getUpdateQueryString(tableName, datasource, monitor);
+                       datasource.executeUpdate(updateQuery);
+
+                       if (defaultValue instanceof Boolean){
+                               String defaultValueQuery = "UPDATE @tableName SET @columnName = " + (defaultValue == null ? "NULL" : getBoolean((Boolean) defaultValue, datasource));
+                               defaultValueQuery = defaultValueQuery.replace("@tableName", tableName);
+                               defaultValueQuery = defaultValueQuery.replace("@columnName", newColumnName);
+                               datasource.executeUpdate(defaultValueQuery);
+                       }else if (defaultValue instanceof Integer){
+                               String defaultValueQuery = "UPDATE @tableName SET @columnName = " + (defaultValue == null ? "NULL" : defaultValue);
+                               defaultValueQuery = defaultValueQuery.replace("@tableName", tableName);
+                               defaultValueQuery = defaultValueQuery.replace("@columnName", newColumnName);
+                               datasource.executeUpdate(defaultValueQuery);
+            }else if (defaultValue instanceof String){
+                String defaultValueQuery = "UPDATE @tableName SET @columnName = " + (defaultValue == null ? "NULL" : "'" + defaultValue + "'");
+                defaultValueQuery = defaultValueQuery.replace("@tableName", tableName);
+                defaultValueQuery = defaultValueQuery.replace("@columnName", newColumnName);
+                datasource.executeUpdate(defaultValueQuery);
+                       }else if (defaultValue != null){
+                               logger.warn("Default Value not implemented for type " + defaultValue.getClass().getName());
+                       }
+                       if (referencedTable != null){
+                               TableCreator.makeForeignKey(tableName, datasource, monitor, newColumnName, referencedTable, caseType, result);
+                       }
+                       return;
+               } catch ( Exception e) {
+                   String message = "Unhandled exception when trying to add column " +
+                           newColumnName + " for table " +  tableName;
+                       monitor.warning(message, e);
+                       logger.error(e);
+                       result.addException(e, message, getStepName());
+                       return;
+               }
+       }
+
+       /**
+        * Returns the update query string. tableName must already be cased correctly. See {@link CaseType}.
+        * @param tableName correctly cased table name
+        * @param datasource data source
+        * @param monitor monitor
+        * @return the query string
+        * @throws DatabaseTypeNotSupportedException
+        */
+       public String getUpdateQueryString(String tableName, ICdmDataSource datasource, IProgressMonitor monitor) throws DatabaseTypeNotSupportedException {
+               String updateQuery;
+               DatabaseTypeEnum type = datasource.getDatabaseType();
+               String databaseColumnType = this.columnType.format(datasource, size, scale);
+
+               if (type.equals(DatabaseTypeEnum.SqlServer2005)){
+                       //MySQL allows both syntaxes
+                       updateQuery = "ALTER TABLE @tableName ADD @columnName @columnType";
+               }else if (type.equals(DatabaseTypeEnum.H2) || type.equals(DatabaseTypeEnum.PostgreSQL) || type.equals(DatabaseTypeEnum.MySQL)){
+                       updateQuery = "ALTER TABLE @tableName @addSeparator @columnName @columnType";
+               }else{
+                       updateQuery = null;
+                       String warning = "Update step '" + this.getStepName() + "' is not supported by " + type.getName();
+                       monitor.warning(warning);
+                       throw new DatabaseTypeNotSupportedException(warning);
+               }
+               if (isNotNull && !isAuditing){
+                       updateQuery += " NOT NULL";
+               }
+               updateQuery = updateQuery.replace("@tableName", tableName);
+               updateQuery = updateQuery.replace("@columnName", newColumnName);
+               updateQuery = updateQuery.replace("@columnType", databaseColumnType);
+               updateQuery = updateQuery.replace("@addSeparator", getAddColumnSeperator(datasource));
+
+               return updateQuery;
+       }
+
+       /**
+        * Returns the sql keywords for adding a column. This is usually 'ADD' or 'ADD COLUMN'
+        * @param datasource
+        * @return
+        * @throws DatabaseTypeNotSupportedException
+        */
+       public static String getAddColumnSeperator(ICdmDataSource datasource) throws DatabaseTypeNotSupportedException {
+               DatabaseTypeEnum type = datasource.getDatabaseType();
+               if (type.equals(DatabaseTypeEnum.SqlServer2005)){
+                       return "ADD ";
+               }else if (type.equals(DatabaseTypeEnum.H2) || type.equals(DatabaseTypeEnum.PostgreSQL) || type.equals(DatabaseTypeEnum.MySQL)){
+                       return "ADD COLUMN ";
+               }else{
+                       throw new DatabaseTypeNotSupportedException(datasource.getName());
+               }
+       }
+
+       public String getReferencedTable() {
+               return referencedTable;
+       }
+
+       public String getNewColumnName() {
+               return newColumnName;
+       }
+}
\ No newline at end of file