Project

General

Profile

Download (9.95 KB) Statistics
| Branch: | Tag: | Revision:
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.sql.SQLException;
12

    
13
import org.apache.log4j.Logger;
14

    
15
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
16
import eu.etaxonomy.cdm.database.DatabaseTypeEnum;
17
import eu.etaxonomy.cdm.database.ICdmDataSource;
18

    
19
/**
20
 * @author a.mueller
21
 * @since 16.09.2010
22
 *
23
 */
24
public class ColumnTypeChanger
25
        extends AuditedSchemaUpdaterStepBase {
26

    
27
    /**
28
     *
29
     */
30
    private static final String _OLDXXX = "_oldxxx";
31

    
32
    private static final Logger logger = Logger.getLogger(ColumnTypeChanger.class);
33

    
34
	private final String columnName;
35
	private final String newColumnType;
36
	private final Object defaultValue;
37
	private final boolean isNotNull;
38
	private final String referencedTable;
39

    
40

    
41
	public static final ColumnTypeChanger NewStringSizeInstance(String stepName, String tableName, String columnName, int newSize, boolean includeAudTable){
42
		return new ColumnTypeChanger(stepName, tableName, columnName, "nvarchar("+newSize+")", includeAudTable, null, false, null);
43
	}
44

    
45
	public static final ColumnTypeChanger NewClobInstance(String stepName, String tableName, String columnName, boolean includeAudTable){
46
		return new ColumnTypeChanger(stepName, tableName, columnName, "clob", includeAudTable, null, false, null);
47
	}
48

    
49
	public static final ColumnTypeChanger NewInt2DoubleInstance(String stepName, String tableName, String columnName, boolean includeAudTable){
50
		return new ColumnTypeChanger(stepName, tableName, columnName, "double", includeAudTable, null, false, null);
51
	}
52

    
53
	public static final ColumnTypeChanger NewInt2StringInstance(String stepName, String tableName, String columnName, int size, boolean includeAudTable, Integer defaultValue, boolean notNull){
54
		return new ColumnTypeChanger(stepName, tableName, columnName, "nvarchar("+size+")", includeAudTable, defaultValue, notNull, null);
55
	}
56

    
57
//	public static final ColumnTypeChanger NewChangeAllowNullOnStringChanger(){
58
//
59
//	}
60

    
61

    
62
	protected ColumnTypeChanger(String stepName, String tableName, String columnName, String newColumnType, boolean includeAudTable, Object defaultValue, boolean notNull, String referencedTable) {
63
		super(stepName, tableName, includeAudTable);
64
		this.columnName = columnName;
65
		this.newColumnType = newColumnType;
66
		this.defaultValue = defaultValue;
67
		this.isNotNull = notNull;
68
		this.referencedTable = referencedTable;
69
	}
70

    
71
    @Override
72
    protected void invokeOnTable(String tableName, ICdmDataSource datasource,
73
            IProgressMonitor monitor, CaseType caseType, SchemaUpdateResult result) {
74
        try {
75

    
76
			String updateQuery;
77

    
78
			//set null values to default value if NOT NULL
79
			if (this.isNotNull){
80
				updateQuery = getNotNullUpdateQuery(tableName);
81
				datasource.executeUpdate(updateQuery);
82
			}
83

    
84
			//update
85
			changeType(tableName, datasource, monitor, caseType, result);
86

    
87
			if (defaultValue instanceof Boolean){
88
				updateQuery = "UPDATE @tableName SET @columnName = " + (defaultValue == null ? "null" : getBoolean((Boolean) defaultValue, datasource)) +
89
				        " WHERE @columnName IS NULL ";
90
				updateQuery = updateQuery.replace("@tableName", tableName);
91
				updateQuery = updateQuery.replace("@columnName", columnName);
92
				datasource.executeUpdate(updateQuery);
93
			}
94

    
95
			//foreign keys
96
			if (referencedTable != null){
97
				TableCreator.makeForeignKey(tableName, datasource, monitor, columnName, referencedTable, caseType, result);
98
			}
99

    
100
			return;
101
		} catch ( Exception e) {
102
		    String message = "Unhandled exception when trying to change column type for " +
103
                    columnName + " for table " +  tableName;
104
            monitor.warning(message, e);
105
            logger.error(e);
106
            result.addException(e, message, getStepName());
107
            return;
108
		}
109
	}
110

    
111
    /**
112
     * @param tableName
113
     * @param datasource
114
     * @param monitor
115
     * @param result
116
     * @param caseType
117
     * @throws DatabaseTypeNotSupportedException
118
     * @throws SQLException
119
     */
120
    protected void changeType(String tableName, ICdmDataSource datasource, IProgressMonitor monitor,
121
            CaseType caseType, SchemaUpdateResult result)
122
            throws DatabaseTypeNotSupportedException, SQLException {
123
        DatabaseTypeEnum type = datasource.getDatabaseType();
124
        if (type.equals(DatabaseTypeEnum.PostgreSQL)){
125
            handlePostgres(tableName, datasource, monitor, caseType, result);
126
        }else{
127
            String updateQuery = getUpdateQueryString(tableName, datasource, monitor);
128
            datasource.executeUpdate(updateQuery);
129
        }
130
    }
131

    
132

    
133
    private String getNotNullUpdateQuery(String tableName) {
134
		String query = " UPDATE %s SET %s = %s WHERE %s IS NULL ";
135
		String defaultValueStr = String.valueOf(this.defaultValue);
136
		if (this.defaultValue instanceof Integer){
137
			//OK
138
		}else{
139
			defaultValueStr = "'" + defaultValueStr + "'";
140
		}
141
		query = String.format(query, tableName, this.columnName, defaultValueStr, this.columnName);
142
		return query;
143
	}
144

    
145
	public String getUpdateQueryString(String tableName, ICdmDataSource datasource, IProgressMonitor monitor) throws DatabaseTypeNotSupportedException {
146
		String updateQuery;
147
		DatabaseTypeEnum type = datasource.getDatabaseType();
148
		String databaseColumnType = getDatabaseColumnType(datasource, this.newColumnType);
149

    
150
		if (type.equals(DatabaseTypeEnum.SqlServer2005)){
151
			//MySQL allows both syntaxes
152
			updateQuery = "ALTER TABLE @tableName ALTER COLUMN @columnName @columnType";
153
		}else if (type.equals(DatabaseTypeEnum.H2)){
154
			updateQuery = "ALTER TABLE @tableName ALTER COLUMN @columnName @columnType";
155
		}else if (type.equals(DatabaseTypeEnum.PostgreSQL)){
156
			updateQuery = "ALTER TABLE @tableName ALTER COLUMN @columnName TYPE @columnType";
157
		}else if (type.equals(DatabaseTypeEnum.MySQL)){
158
			updateQuery = "ALTER TABLE @tableName MODIFY COLUMN @columnName @columnType";
159
		}else{
160
			updateQuery = null;
161
			String warning = "Update step '" + this.getStepName() + "' is not supported by " + type.getName();
162
			monitor.warning(warning);
163
			throw new DatabaseTypeNotSupportedException(warning);
164
		}
165
		if (isNotNull){
166
			if (datasource.getDatabaseType().equals(DatabaseTypeEnum.PostgreSQL)){
167
				logger.warn("NOT NULL not implementd for POSTGRES");
168
			}else{
169
				updateQuery += " NOT NULL";
170
			}
171
		} else{
172
			if (! datasource.getDatabaseType().equals(DatabaseTypeEnum.PostgreSQL)){
173
				updateQuery += " NULL ";
174
			}
175
		}
176
		updateQuery = updateQuery.replace("@tableName", tableName);
177
		updateQuery = updateQuery.replace("@columnName", columnName);
178
		updateQuery = updateQuery.replace("@columnType", databaseColumnType);
179
//		updateQuery = updateQuery.replace("@addSeparator", getAddColumnSeperator(datasource));
180

    
181
		return updateQuery;
182
	}
183

    
184

    
185
    /**
186
     * Postgres has problems with datatype changes as casting does often not really work even if using " ... USING ...."
187
     * resulting in errors like "operator does not exist: character varying >= integer".
188
     * Therefore we better create a new column here and transfer the data from the old column to the new column.
189
     * @param tableName
190
     * @param datasource
191
     * @param monitor
192
     * @param result
193
     * @param caseType
194
     * @throws SQLException
195
     */
196
    private void handlePostgres(String tableName, ICdmDataSource datasource, IProgressMonitor monitor, CaseType caseType, SchemaUpdateResult result) throws SQLException {
197
        //
198
        boolean includeAuditing = false;
199
        String colNameChanged = this.columnName + _OLDXXX;
200
        String databaseColumnType = getDatabaseColumnType(datasource, this.newColumnType);
201

    
202
        //change old column name
203
        //note data type is not relevant for ColumnNameChanger with Postgres
204
        ISchemaUpdaterStep step = ColumnNameChanger.NewIntegerInstance(this.stepName + " - Change column name",
205
                tableName, this.columnName, colNameChanged, includeAuditing);
206
        step.invoke(datasource, monitor, caseType, result);
207

    
208
        //create new column
209
//        step = ColumnAdder.NewStringInstance(this.stepName + " - Add new column", tableName, this.columnName, includeAuditing);
210
        Object defaultValue = null;
211
        step = new ColumnAdder(this.stepName + " - Add new column", tableName, this.columnName, newColumnType, includeAuditing, defaultValue, false, null);
212
        step.invoke(datasource, monitor, caseType, result);
213

    
214
        //move data
215
        String updateQuery = " UPDATE @tableName SET @columnName = @columnOld::@type ";
216
        String casedTableName = caseType.transformTo(tableName);
217
        updateQuery = updateQuery.replace("@tableName", casedTableName);
218
        updateQuery = updateQuery.replace("@columnName", columnName);
219
        updateQuery = updateQuery.replace("@columnOld", colNameChanged);
220
        updateQuery = updateQuery.replace("@type", databaseColumnType);
221
//        if (this.isAuditing){
222
//            step = SimpleSchemaUpdaterStep.NewAuditedInstance(this.stepName + " - Move data", updateQuery, casedTableName, -99);
223
//        }else{
224
            step = SimpleSchemaUpdaterStep.NewNonAuditedInstance(this.stepName + " - Move data", updateQuery, -99);
225
//        }
226
        step.invoke(datasource, monitor, caseType, result);
227

    
228
        //delete old column
229
        step = ColumnRemover.NewInstance(this.stepName + " - Remove old column", tableName, colNameChanged, includeAuditing);
230
        step.invoke(datasource, monitor, caseType, result);
231
    }
232

    
233
	private String getDatabaseColumnType(ICdmDataSource datasource, String columnType) {
234
		return ColumnAdder.getDatabaseColumnType(datasource, columnType);
235
	}
236

    
237
	public String getReferencedTable() {
238
		return referencedTable;
239
	}
240
//
241
//	public String getNewColumnName() {
242
//		return columnName;
243
//	}
244

    
245
}
(9-9/36)