2 * Copyright (C) 2009 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.cdm
.database
.update
;
11 import java
.sql
.Types
;
13 import org
.apache
.log4j
.Logger
;
15 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
16 import eu
.etaxonomy
.cdm
.database
.DatabaseTypeEnum
;
17 import eu
.etaxonomy
.cdm
.database
.ICdmDataSource
;
24 public class ColumnAdder
extends AuditedSchemaUpdaterStepBase
{
25 private static final Logger logger
= Logger
.getLogger(ColumnAdder
.class);
27 private final String newColumnName
;
28 private final String columnType
;
29 private final Object defaultValue
;
30 private boolean isNotNull
;
31 private final String referencedTable
;
35 * @param referencedTable
38 public static final ColumnAdder
NewIntegerInstance(String stepName
, String tableName
, String newColumnName
, boolean includeAudTable
, boolean notNull
, String referencedTable
){
39 return new ColumnAdder(stepName
, tableName
, newColumnName
, "int", includeAudTable
, null, notNull
, referencedTable
);
42 public static final ColumnAdder
NewIntegerInstance(String stepName
, String tableName
, String newColumnName
, boolean includeAudTable
, Integer defaultValue
, boolean notNull
){
43 return new ColumnAdder(stepName
, tableName
, newColumnName
, "int", includeAudTable
, defaultValue
, notNull
, null);
46 public static final ColumnAdder
NewTinyIntegerInstance(String stepName
, String tableName
, String newColumnName
, boolean includeAudTable
, boolean notNull
){
47 return new ColumnAdder(stepName
, tableName
, newColumnName
, "tinyint", includeAudTable
, null, notNull
, null);
50 public static final ColumnAdder
NewDoubleInstance(String stepName
, String tableName
, String newColumnName
, boolean includeAudTable
, boolean notNull
){
51 return new ColumnAdder(stepName
, tableName
, newColumnName
, "double", includeAudTable
, null, notNull
, null);
54 public static final ColumnAdder
NewBooleanInstance(String stepName
, String tableName
, String newColumnName
, boolean includeAudTable
, Boolean defaultValue
){
55 return new ColumnAdder(stepName
, tableName
, newColumnName
, "bit", includeAudTable
, defaultValue
, false, null);
59 * Adds a string column with length 255 and default value <code>null</code>
61 public static final ColumnAdder
NewStringInstance(String stepName
, String tableName
, String newColumnName
, boolean includeAudTable
){
62 return new ColumnAdder(stepName
, tableName
, newColumnName
, "nvarchar(255)", includeAudTable
, null, false, null);
65 public static final ColumnAdder
NewStringInstance(String stepName
, String tableName
, String newColumnName
, int length
, boolean includeAudTable
){
66 return new ColumnAdder(stepName
, tableName
, newColumnName
, "nvarchar("+length
+")", includeAudTable
, null, false, null);
68 public static final ColumnAdder
NewStringInstance(String stepName
, String tableName
, String newColumnName
, int length
, String defaultValue
, boolean includeAudTable
){
69 return new ColumnAdder(stepName
, tableName
, newColumnName
, "nvarchar("+length
+")", includeAudTable
, defaultValue
, false, null);
72 public static final ColumnAdder
NewClobInstance(String stepName
, String tableName
, String newColumnName
, boolean includeAudTable
){
73 return new ColumnAdder(stepName
, tableName
, newColumnName
, "clob", includeAudTable
, null, false, null);
76 public static final ColumnAdder
NewDateTimeInstance(String stepName
, String tableName
, String newColumnName
, boolean includeAudTable
, boolean notNull
){
77 return new ColumnAdder(stepName
, tableName
, newColumnName
, "datetime", includeAudTable
, null, notNull
, null);
80 protected ColumnAdder(String stepName
, String tableName
, String newColumnName
, String columnType
, boolean includeAudTable
, Object defaultValue
, boolean notNull
, String referencedTable
) {
81 super(stepName
, tableName
, includeAudTable
);
82 this.newColumnName
= newColumnName
;
83 this.columnType
= columnType
;
84 this.defaultValue
= defaultValue
;
85 this.isNotNull
= notNull
;
86 this.referencedTable
= referencedTable
;
89 public ColumnAdder
setNotNull(boolean isNotNull
) {
90 this.isNotNull
= isNotNull
;
95 protected void invokeOnTable(String tableName
, ICdmDataSource datasource
,
96 IProgressMonitor monitor
, CaseType caseType
, SchemaUpdateResult result
) {
99 String updateQuery
= getUpdateQueryString(tableName
, datasource
, monitor
);
100 datasource
.executeUpdate(updateQuery
);
102 if (defaultValue
instanceof Boolean
){
103 String defaultValueQuery
= "UPDATE @tableName SET @columnName = " + (defaultValue
== null ?
"NULL" : getBoolean((Boolean
) defaultValue
, datasource
));
104 defaultValueQuery
= defaultValueQuery
.replace("@tableName", tableName
);
105 defaultValueQuery
= defaultValueQuery
.replace("@columnName", newColumnName
);
106 datasource
.executeUpdate(defaultValueQuery
);
107 }else if (defaultValue
instanceof Integer
){
108 String defaultValueQuery
= "UPDATE @tableName SET @columnName = " + (defaultValue
== null ?
"NULL" : defaultValue
);
109 defaultValueQuery
= defaultValueQuery
.replace("@tableName", tableName
);
110 defaultValueQuery
= defaultValueQuery
.replace("@columnName", newColumnName
);
111 datasource
.executeUpdate(defaultValueQuery
);
112 }else if (defaultValue
instanceof String
){
113 String defaultValueQuery
= "UPDATE @tableName SET @columnName = " + (defaultValue
== null ?
"NULL" : "'" + defaultValue
+ "'");
114 defaultValueQuery
= defaultValueQuery
.replace("@tableName", tableName
);
115 defaultValueQuery
= defaultValueQuery
.replace("@columnName", newColumnName
);
116 datasource
.executeUpdate(defaultValueQuery
);
117 }else if (defaultValue
!= null){
118 logger
.warn("Default Value not implemented for type " + defaultValue
.getClass().getName());
120 if (referencedTable
!= null){
121 TableCreator
.makeForeignKey(tableName
, datasource
, monitor
, newColumnName
, referencedTable
, caseType
, result
);
124 } catch ( Exception e
) {
125 String message
= "Unhandled exception when trying to add column " +
126 newColumnName
+ " for table " + tableName
;
127 monitor
.warning(message
, e
);
129 result
.addException(e
, message
, getStepName());
135 * Returns the update query string. tableName must already be cased correctly. See {@link CaseType}.
136 * @param tableName correctly cased table name
137 * @param datasource data source
138 * @param monitor monitor
139 * @return the query string
140 * @throws DatabaseTypeNotSupportedException
142 public String
getUpdateQueryString(String tableName
, ICdmDataSource datasource
, IProgressMonitor monitor
) throws DatabaseTypeNotSupportedException
{
144 DatabaseTypeEnum type
= datasource
.getDatabaseType();
145 String databaseColumnType
= getDatabaseColumnType(datasource
, this.columnType
);
147 if (type
.equals(DatabaseTypeEnum
.SqlServer2005
)){
148 //MySQL allows both syntaxes
149 updateQuery
= "ALTER TABLE @tableName ADD @columnName @columnType";
150 }else if (type
.equals(DatabaseTypeEnum
.H2
) || type
.equals(DatabaseTypeEnum
.PostgreSQL
) || type
.equals(DatabaseTypeEnum
.MySQL
)){
151 updateQuery
= "ALTER TABLE @tableName @addSeparator @columnName @columnType";
154 String warning
= "Update step '" + this.getStepName() + "' is not supported by " + type
.getName();
155 monitor
.warning(warning
);
156 throw new DatabaseTypeNotSupportedException(warning
);
159 updateQuery
+= " NOT NULL";
161 updateQuery
= updateQuery
.replace("@tableName", tableName
);
162 updateQuery
= updateQuery
.replace("@columnName", newColumnName
);
163 updateQuery
= updateQuery
.replace("@columnType", databaseColumnType
);
164 updateQuery
= updateQuery
.replace("@addSeparator", getAddColumnSeperator(datasource
));
169 protected static String
getDatabaseColumnType(ICdmDataSource datasource
, String columnType
) {
170 String result
= columnType
;
171 DatabaseTypeEnum dbType
= datasource
.getDatabaseType();
173 if (dbType
.equals(DatabaseTypeEnum
.PostgreSQL
)){ //TODO use PostgeSQL82 Dialect infos
174 result
= result
.replace("nvarchar", "varchar");
175 result
= result
.replace("double", "float8");
176 result
= result
.replace("bit", DatabaseTypeEnum
.PostgreSQL
.getHibernateDialect().getTypeName(Types
.BIT
));
177 result
= result
.replace("datetime", DatabaseTypeEnum
.PostgreSQL
.getHibernateDialect().getTypeName(Types
.TIMESTAMP
));
178 result
= result
.replace("tinyint", DatabaseTypeEnum
.PostgreSQL
.getHibernateDialect().getTypeName(Types
.TINYINT
));
181 if (columnType
.equalsIgnoreCase("clob")){
182 //TODO use hibernate dialects
183 if (dbType
.equals(DatabaseTypeEnum
.MySQL
)){
185 }else if (dbType
.equals(DatabaseTypeEnum
.H2
)){
186 result
= "CLOB"; //or NVARCHAR
187 }else if (dbType
.equals(DatabaseTypeEnum
.PostgreSQL
)){
189 }else if (dbType
.equals(DatabaseTypeEnum
.SqlServer2005
)){
190 result
= "NVARCHAR(MAX)";
198 * Returns the sql keywords for adding a column. This is usually 'ADD' or 'ADD COLUMN'
201 * @throws DatabaseTypeNotSupportedException
203 public static String
getAddColumnSeperator(ICdmDataSource datasource
) throws DatabaseTypeNotSupportedException
{
204 DatabaseTypeEnum type
= datasource
.getDatabaseType();
205 if (type
.equals(DatabaseTypeEnum
.SqlServer2005
)){
207 }else if (type
.equals(DatabaseTypeEnum
.H2
) || type
.equals(DatabaseTypeEnum
.PostgreSQL
) || type
.equals(DatabaseTypeEnum
.MySQL
)){
208 return "ADD COLUMN ";
210 throw new DatabaseTypeNotSupportedException(datasource
.getName());
214 public String
getReferencedTable() {
215 return referencedTable
;
218 public String
getNewColumnName() {
219 return newColumnName
;