Merge branch 'release/5.44.0'
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / database / update / ColumnAdder.java
1 /**
2 * Copyright (C) 2009 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.cdm.database.update;
10
11 import java.util.List;
12
13 import org.apache.logging.log4j.LogManager;
14 import org.apache.logging.log4j.Logger;
15
16 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
17 import eu.etaxonomy.cdm.database.DatabaseTypeEnum;
18 import eu.etaxonomy.cdm.database.ICdmDataSource;
19
20 /**
21 * @author a.mueller
22 * @since 16.09.2010
23 */
24 public class ColumnAdder extends AuditedSchemaUpdaterStepBase {
25
26 private static final Logger logger = LogManager.getLogger();
27
28 private final String newColumnName;
29 private final Datatype columnType;
30 private final Integer size;
31 private final Integer scale;
32 private final Object defaultValue;
33 private boolean isNotNull;
34 private final String referencedTable;
35
36 /**
37 * Add ForeignKey.
38 */
39 public static final ColumnAdder NewIntegerInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull, String referencedTable){
40 return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.INTEGER, null, null, includeAudTable, null, notNull, referencedTable);
41 }
42
43 public static final ColumnAdder NewIntegerInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, Integer defaultValue, boolean notNull){
44 return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.INTEGER, null, null, includeAudTable, defaultValue, notNull, null);
45 }
46
47 public static final ColumnAdder NewTinyIntegerInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){
48 return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.TINYINTEGER, null, null, includeAudTable, null, notNull, null);
49 }
50
51 public static final ColumnAdder NewDoubleInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){
52 return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.DOUBLE, null, null, includeAudTable, null, notNull, null);
53 }
54
55 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){
56 return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.BIGDECIMAL, precision, scale, includeAudTable, defaultValue, notNull, null);
57 }
58
59 public static final ColumnAdder NewBooleanInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, Boolean defaultValue){
60 return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.BIT, null, null, includeAudTable, defaultValue, false, null);
61 }
62
63 /**
64 * Adds a string column with length 255 and default value <code>null</code>
65 */
66 public static final ColumnAdder NewStringInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable){
67 return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, 255, null, includeAudTable, null, false, null);
68 }
69
70 public static final ColumnAdder NewDTYPEInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String defaultValue, boolean includeAudTable){
71 return new ColumnAdder(stepList, stepName, tableName, "DTYPE", Datatype.VARCHAR, 31, null, includeAudTable, defaultValue, true, null);
72 }
73
74 /**
75 * Adds a string column with the given length and default value <code>null</code>
76 */
77 public static final ColumnAdder NewStringInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, int length, boolean includeAudTable){
78 return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, length, null, includeAudTable, null, false, null);
79 }
80
81 /**
82 * Adds a string column with the given length and the given default value
83 */
84 public static final ColumnAdder NewStringInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, int length, String defaultValue, boolean includeAudTable){
85 return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, length, null, includeAudTable, defaultValue, false, null);
86 }
87
88 public static final ColumnAdder NewClobInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable){
89 return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.CLOB, null, null, includeAudTable, null, false, null);
90 }
91
92 public static final ColumnAdder NewDateTimeInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){
93 return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.DATETIME, null, null, includeAudTable, null, notNull, null);
94 }
95
96 protected ColumnAdder(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName,
97 Datatype columnType, Integer size, Integer scale, boolean includeAudTable, Object defaultValue, boolean notNull, String referencedTable) {
98 super(stepList, stepName, tableName, includeAudTable);
99 this.newColumnName = newColumnName;
100 this.columnType = columnType;
101 this.size = size;
102 this.defaultValue = defaultValue;
103 this.isNotNull = notNull;
104 this.referencedTable = referencedTable;
105 this.scale = scale;
106 }
107
108 public ColumnAdder setNotNull(boolean isNotNull) {
109 this.isNotNull = isNotNull;
110 return this;
111 }
112
113 @Override
114 protected void invokeOnTable(String tableName, ICdmDataSource datasource,
115 IProgressMonitor monitor, CaseType caseType, SchemaUpdateResult result) {
116
117 try {
118 String updateQuery = getUpdateQueryString(tableName, datasource, monitor);
119 datasource.executeUpdate(updateQuery);
120
121 if (defaultValue instanceof Boolean){
122 String defaultValueQuery = "UPDATE @tableName SET @columnName = " + (defaultValue == null ? "NULL" : getBoolean((Boolean) defaultValue, datasource));
123 defaultValueQuery = defaultValueQuery.replace("@tableName", tableName);
124 defaultValueQuery = defaultValueQuery.replace("@columnName", newColumnName);
125 datasource.executeUpdate(defaultValueQuery);
126 }else if (defaultValue instanceof Integer){
127 String defaultValueQuery = "UPDATE @tableName SET @columnName = " + (defaultValue == null ? "NULL" : defaultValue);
128 defaultValueQuery = defaultValueQuery.replace("@tableName", tableName);
129 defaultValueQuery = defaultValueQuery.replace("@columnName", newColumnName);
130 datasource.executeUpdate(defaultValueQuery);
131 }else if (defaultValue instanceof String){
132 String defaultValueQuery = "UPDATE @tableName SET @columnName = " + (defaultValue == null ? "NULL" : "'" + defaultValue + "'");
133 defaultValueQuery = defaultValueQuery.replace("@tableName", tableName);
134 defaultValueQuery = defaultValueQuery.replace("@columnName", newColumnName);
135 datasource.executeUpdate(defaultValueQuery);
136 }else if (defaultValue != null){
137 logger.warn("Default Value not implemented for type " + defaultValue.getClass().getName());
138 }
139 if (referencedTable != null){
140 TableCreator.makeForeignKey(tableName, datasource, monitor, newColumnName, referencedTable, caseType, result);
141 }
142 return;
143 } catch ( Exception e) {
144 String message = "Unhandled exception when trying to add column " +
145 newColumnName + " for table " + tableName;
146 monitor.warning(message, e);
147 logger.error(e);
148 result.addException(e, message, getStepName());
149 return;
150 }
151 }
152
153 /**
154 * Returns the update query string. tableName must already be cased correctly. See {@link CaseType}.
155 * @param tableName correctly cased table name
156 * @param datasource data source
157 * @param monitor monitor
158 * @return the query string
159 * @throws DatabaseTypeNotSupportedException
160 */
161 public String getUpdateQueryString(String tableName, ICdmDataSource datasource, IProgressMonitor monitor) throws DatabaseTypeNotSupportedException {
162 String updateQuery;
163 DatabaseTypeEnum type = datasource.getDatabaseType();
164 String databaseColumnType = this.columnType.format(datasource, size, scale);
165
166 if (type.equals(DatabaseTypeEnum.SqlServer2005)){
167 //MySQL allows both syntaxes
168 updateQuery = "ALTER TABLE @tableName ADD @columnName @columnType";
169 }else if (type.equals(DatabaseTypeEnum.H2) || type.equals(DatabaseTypeEnum.PostgreSQL) || type.equals(DatabaseTypeEnum.MySQL)){
170 updateQuery = "ALTER TABLE @tableName @addSeparator @columnName @columnType";
171 }else{
172 updateQuery = null;
173 String warning = "Update step '" + this.getStepName() + "' is not supported by " + type.getName();
174 monitor.warning(warning);
175 throw new DatabaseTypeNotSupportedException(warning);
176 }
177 if (isNotNull && !isAuditing){
178 updateQuery += " NOT NULL";
179 }
180 updateQuery = updateQuery.replace("@tableName", tableName);
181 updateQuery = updateQuery.replace("@columnName", newColumnName);
182 updateQuery = updateQuery.replace("@columnType", databaseColumnType);
183 updateQuery = updateQuery.replace("@addSeparator", getAddColumnSeperator(datasource));
184
185 return updateQuery;
186 }
187
188 /**
189 * Returns the sql keywords for adding a column. This is usually 'ADD' or 'ADD COLUMN'
190 * @param datasource
191 * @return
192 * @throws DatabaseTypeNotSupportedException
193 */
194 public static String getAddColumnSeperator(ICdmDataSource datasource) throws DatabaseTypeNotSupportedException {
195 DatabaseTypeEnum type = datasource.getDatabaseType();
196 if (type.equals(DatabaseTypeEnum.SqlServer2005)){
197 return "ADD ";
198 }else if (type.equals(DatabaseTypeEnum.H2) || type.equals(DatabaseTypeEnum.PostgreSQL) || type.equals(DatabaseTypeEnum.MySQL)){
199 return "ADD COLUMN ";
200 }else{
201 throw new DatabaseTypeNotSupportedException(datasource.getName());
202 }
203 }
204
205 public String getReferencedTable() {
206 return referencedTable;
207 }
208
209 public String getNewColumnName() {
210 return newColumnName;
211 }
212 }