001
014
015 package com.liferay.portal.kernel.upgrade;
016
017 import com.liferay.portal.kernel.dao.db.BaseDBProcess;
018 import com.liferay.portal.kernel.dao.db.DB;
019 import com.liferay.portal.kernel.dao.db.DBManagerUtil;
020 import com.liferay.portal.kernel.dao.db.DBProcessContext;
021 import com.liferay.portal.kernel.dao.db.IndexMetadata;
022 import com.liferay.portal.kernel.dao.db.IndexMetadataFactoryUtil;
023 import com.liferay.portal.kernel.dao.jdbc.DataAccess;
024 import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
025 import com.liferay.portal.kernel.log.Log;
026 import com.liferay.portal.kernel.log.LogFactoryUtil;
027 import com.liferay.portal.kernel.upgrade.util.UpgradeColumn;
028 import com.liferay.portal.kernel.upgrade.util.UpgradeTable;
029 import com.liferay.portal.kernel.upgrade.util.UpgradeTableFactoryUtil;
030 import com.liferay.portal.kernel.util.ArrayUtil;
031 import com.liferay.portal.kernel.util.ClassUtil;
032 import com.liferay.portal.kernel.util.LoggingTimer;
033 import com.liferay.portal.kernel.util.ObjectValuePair;
034 import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
035 import com.liferay.portal.kernel.util.StringBundler;
036 import com.liferay.portal.kernel.util.StringPool;
037 import com.liferay.portal.kernel.util.StringUtil;
038
039 import java.io.IOException;
040 import java.io.InputStream;
041 import java.io.InputStreamReader;
042 import java.io.Reader;
043
044 import java.lang.reflect.Field;
045
046 import java.sql.Connection;
047 import java.sql.DatabaseMetaData;
048 import java.sql.ResultSet;
049 import java.sql.SQLException;
050
051 import java.util.ArrayList;
052 import java.util.HashMap;
053 import java.util.HashSet;
054 import java.util.List;
055 import java.util.Map;
056 import java.util.Set;
057
058
062 public abstract class UpgradeProcess
063 extends BaseDBProcess implements UpgradeStep {
064
065 public void clearIndexesCache() {
066 _portalIndexesSQL.clear();
067 }
068
069 public int getThreshold() {
070
071
072
073
074
075 return 0;
076 }
077
078 public void upgrade() throws UpgradeException {
079 long start = System.currentTimeMillis();
080
081 if (_log.isInfoEnabled()) {
082 _log.info("Upgrading " + ClassUtil.getClassName(this));
083 }
084
085 try (Connection con = DataAccess.getUpgradeOptimizedConnection()) {
086 connection = con;
087
088 doUpgrade();
089 }
090 catch (Exception e) {
091 throw new UpgradeException(e);
092 }
093 finally {
094 connection = null;
095
096 if (_log.isInfoEnabled()) {
097 _log.info(
098 "Completed upgrade process " +
099 ClassUtil.getClassName(this) + " in " +
100 (System.currentTimeMillis() - start) + "ms");
101 }
102 }
103 }
104
105 public void upgrade(Class<?> upgradeProcessClass) throws UpgradeException {
106 UpgradeProcess upgradeProcess = null;
107
108 try {
109 upgradeProcess = (UpgradeProcess)upgradeProcessClass.newInstance();
110 }
111 catch (Exception e) {
112 throw new UpgradeException(e);
113 }
114
115 upgradeProcess.upgrade();
116 }
117
118 @Override
119 public void upgrade(DBProcessContext dbProcessContext)
120 throws UpgradeException {
121
122 upgrade();
123 }
124
125 public void upgrade(UpgradeProcess upgradeProcess) throws UpgradeException {
126 upgradeProcess.upgrade();
127 }
128
129 public interface Alterable {
130
131 public String getIndexedColumnName();
132
133 public String getSQL(String tableName);
134
135 }
136
137 public class AlterColumnName implements Alterable {
138
139 public AlterColumnName(String oldColumnName, String newColumn) {
140 _oldColumnName = oldColumnName;
141 _newColumn = newColumn;
142 }
143
144 @Override
145 public String getIndexedColumnName() {
146 return _oldColumnName;
147 }
148
149 @Override
150 public String getSQL(String tableName) {
151 StringBundler sb = new StringBundler(6);
152
153 sb.append("alter_column_name ");
154 sb.append(tableName);
155 sb.append(StringPool.SPACE);
156 sb.append(_oldColumnName);
157 sb.append(StringPool.SPACE);
158 sb.append(_newColumn);
159
160 return sb.toString();
161 }
162
163 private final String _newColumn;
164 private final String _oldColumnName;
165
166 }
167
168 public class AlterColumnType implements Alterable {
169
170 public AlterColumnType(String columnName, String newType) {
171 _columnName = columnName;
172 _newType = newType;
173 }
174
175 @Override
176 public String getIndexedColumnName() {
177 return _columnName;
178 }
179
180 @Override
181 public String getSQL(String tableName) {
182 StringBundler sb = new StringBundler(6);
183
184 sb.append("alter_column_type ");
185 sb.append(tableName);
186 sb.append(StringPool.SPACE);
187 sb.append(_columnName);
188 sb.append(StringPool.SPACE);
189 sb.append(_newType);
190
191 return sb.toString();
192 }
193
194 private final String _columnName;
195 private final String _newType;
196
197 }
198
199 public class AlterTableAddColumn implements Alterable {
200
201 public AlterTableAddColumn(String columnName) {
202 _columnName = columnName;
203 }
204
205 @Override
206 public String getIndexedColumnName() {
207 return null;
208 }
209
210 @Override
211 public String getSQL(String tableName) {
212 StringBundler sb = new StringBundler(4);
213
214 sb.append("alter table ");
215 sb.append(tableName);
216 sb.append(" add ");
217 sb.append(_columnName);
218
219 return sb.toString();
220 }
221
222 private final String _columnName;
223
224 }
225
226 public class AlterTableDropColumn implements Alterable {
227
228 public AlterTableDropColumn(String columnName) {
229 _columnName = columnName;
230 }
231
232 @Override
233 public String getIndexedColumnName() {
234 return _columnName;
235 }
236
237 @Override
238 public String getSQL(String tableName) {
239 StringBundler sb = new StringBundler(4);
240
241 sb.append("alter table ");
242 sb.append(tableName);
243 sb.append(" drop column ");
244 sb.append(_columnName);
245
246 return sb.toString();
247 }
248
249 private final String _columnName;
250
251 }
252
253 protected void alter(Class<?> tableClass, Alterable... alterables)
254 throws Exception {
255
256 try (LoggingTimer loggingTimer = new LoggingTimer()) {
257 Field tableNameField = tableClass.getField("TABLE_NAME");
258
259 String tableName = (String)tableNameField.get(null);
260
261 DatabaseMetaData databaseMetaData = connection.getMetaData();
262
263 try (ResultSet rs1 = databaseMetaData.getPrimaryKeys(
264 null, null, tableName);
265 ResultSet rs2 = databaseMetaData.getIndexInfo(
266 null, null, normalizeName(tableName, databaseMetaData),
267 false, false)) {
268
269 Set<String> primaryKeyNames = new HashSet<>();
270
271 while (rs1.next()) {
272 String primaryKeyName = rs1.getString("PK_NAME");
273
274 if (primaryKeyName != null) {
275 primaryKeyNames.add(primaryKeyName);
276 }
277 }
278
279 Map<String, Set<String>> columnNamesMap = new HashMap<>();
280
281 while (rs2.next()) {
282 String indexName = rs2.getString("INDEX_NAME");
283
284 if ((indexName == null) ||
285 primaryKeyNames.contains(indexName)) {
286
287 continue;
288 }
289
290 Set<String> columnNames = columnNamesMap.get(indexName);
291
292 if (columnNames == null) {
293 columnNames = new HashSet<>();
294
295 columnNamesMap.put(indexName, columnNames);
296 }
297
298 columnNames.add(rs2.getString("COLUMN_NAME"));
299 }
300
301 for (Alterable alterable : alterables) {
302 String columnName = alterable.getIndexedColumnName();
303
304 for (Map.Entry<String, Set<String>> entry :
305 columnNamesMap.entrySet()) {
306
307 Set<String> columnNames = entry.getValue();
308
309 if (columnNames.contains(columnName)) {
310 runSQL(
311 "drop index " + entry.getKey() + " on " +
312 tableName);
313 }
314 }
315
316 runSQL(alterable.getSQL(tableName));
317
318 List<ObjectValuePair<String, IndexMetadata>>
319 objectValuePairs = getIndexesSQL(
320 tableClass.getClassLoader(), tableName);
321
322 if (objectValuePairs == null) {
323 continue;
324 }
325
326 for (ObjectValuePair<String, IndexMetadata>
327 objectValuePair : objectValuePairs) {
328
329 IndexMetadata indexMetadata =
330 objectValuePair.getValue();
331
332 if (!ArrayUtil.contains(
333 indexMetadata.getColumnNames(), columnName)) {
334
335 continue;
336 }
337
338 runSQLTemplateString(
339 objectValuePair.getKey(), false, true);
340 }
341 }
342 }
343 catch (SQLException sqle) {
344 if (_log.isWarnEnabled()) {
345 _log.warn("Fallback to recreating the table", sqle);
346 }
347
348 Field tableColumnsField = tableClass.getField("TABLE_COLUMNS");
349 Field tableSQLCreateField = tableClass.getField(
350 "TABLE_SQL_CREATE");
351 Field tableSQLAddIndexesField = tableClass.getField(
352 "TABLE_SQL_ADD_INDEXES");
353
354 upgradeTable(
355 tableName, (Object[][])tableColumnsField.get(null),
356 (String)tableSQLCreateField.get(null),
357 (String[])tableSQLAddIndexesField.get(null));
358 }
359 }
360 }
361
362 protected abstract void doUpgrade() throws Exception;
363
364 protected List<ObjectValuePair<String, IndexMetadata>> getIndexesSQL(
365 ClassLoader classLoader, String tableName)
366 throws IOException {
367
368 if (!PortalClassLoaderUtil.isPortalClassLoader(classLoader)) {
369 List<ObjectValuePair<String, IndexMetadata>> objectValuePairs =
370 new ArrayList<>();
371
372 try (InputStream is = classLoader.getResourceAsStream(
373 "META-INF/sql/indexes.sql");
374 Reader reader = new InputStreamReader(is);
375 UnsyncBufferedReader unsyncBufferedReader =
376 new UnsyncBufferedReader(reader)) {
377
378 String line = null;
379
380 while ((line = unsyncBufferedReader.readLine()) != null) {
381 line = line.trim();
382
383 if (line.isEmpty()) {
384 continue;
385 }
386
387 IndexMetadata indexMetadata =
388 IndexMetadataFactoryUtil.createIndexMetadata(line);
389
390 if (tableName.equals(indexMetadata.getTableName())) {
391 objectValuePairs.add(
392 new ObjectValuePair<>(line, indexMetadata));
393 }
394 }
395 }
396
397 return objectValuePairs;
398 }
399
400 if (!_portalIndexesSQL.isEmpty()) {
401 return _portalIndexesSQL.get(tableName);
402 }
403
404 try (InputStream is = classLoader.getResourceAsStream(
405 "com/liferay/portal/tools/sql/dependencies/indexes.sql");
406 Reader reader = new InputStreamReader(is);
407 UnsyncBufferedReader unsyncBufferedReader =
408 new UnsyncBufferedReader(reader)) {
409
410 String line = null;
411
412 while ((line = unsyncBufferedReader.readLine()) != null) {
413 line = line.trim();
414
415 if (line.isEmpty()) {
416 continue;
417 }
418
419 IndexMetadata indexMetadata =
420 IndexMetadataFactoryUtil.createIndexMetadata(line);
421
422 List<ObjectValuePair<String, IndexMetadata>> objectValuePairs =
423 _portalIndexesSQL.get(indexMetadata.getTableName());
424
425 if (objectValuePairs == null) {
426 objectValuePairs = new ArrayList<>();
427
428 _portalIndexesSQL.put(
429 indexMetadata.getTableName(), objectValuePairs);
430 }
431
432 objectValuePairs.add(
433 new ObjectValuePair<>(line, indexMetadata));
434 }
435 }
436
437 return _portalIndexesSQL.get(tableName);
438 }
439
440 protected long increment() {
441 DB db = DBManagerUtil.getDB();
442
443 return db.increment();
444 }
445
446 protected long increment(String name) {
447 DB db = DBManagerUtil.getDB();
448
449 return db.increment(name);
450 }
451
452 protected long increment(String name, int size) {
453 DB db = DBManagerUtil.getDB();
454
455 return db.increment(name, size);
456 }
457
458 protected boolean isSupportsAlterColumnName() {
459 DB db = DBManagerUtil.getDB();
460
461 return db.isSupportsAlterColumnName();
462 }
463
464 protected boolean isSupportsAlterColumnType() {
465 DB db = DBManagerUtil.getDB();
466
467 return db.isSupportsAlterColumnType();
468 }
469
470 protected boolean isSupportsStringCaseSensitiveQuery() {
471 DB db = DBManagerUtil.getDB();
472
473 return db.isSupportsStringCaseSensitiveQuery();
474 }
475
476 protected boolean isSupportsUpdateWithInnerJoin() {
477 DB db = DBManagerUtil.getDB();
478
479 return db.isSupportsUpdateWithInnerJoin();
480 }
481
482 protected String normalizeName(
483 String name, DatabaseMetaData databaseMetaData)
484 throws SQLException {
485
486 if (databaseMetaData.storesLowerCaseIdentifiers()) {
487 return StringUtil.toLowerCase(name);
488 }
489
490 if (databaseMetaData.storesUpperCaseIdentifiers()) {
491 return StringUtil.toUpperCase(name);
492 }
493
494 return name;
495 }
496
497 protected void upgradeTable(String tableName, Object[][] tableColumns)
498 throws Exception {
499
500 UpgradeTable upgradeTable = UpgradeTableFactoryUtil.getUpgradeTable(
501 tableName, tableColumns);
502
503 upgradeTable.updateTable();
504 }
505
506 protected void upgradeTable(
507 String tableName, Object[][] tableColumns, String createSQL,
508 String[] indexesSQL, UpgradeColumn... upgradeColumns)
509 throws Exception {
510
511 try (LoggingTimer loggingTimer = new LoggingTimer(tableName)) {
512 UpgradeTable upgradeTable = UpgradeTableFactoryUtil.getUpgradeTable(
513 tableName, tableColumns, upgradeColumns);
514
515 upgradeTable.setCreateSQL(createSQL);
516 upgradeTable.setIndexesSQL(indexesSQL);
517
518 upgradeTable.updateTable();
519 }
520 }
521
522 private static final Log _log = LogFactoryUtil.getLog(UpgradeProcess.class);
523
524 private static final Map
525 <String, List<ObjectValuePair<String, IndexMetadata>>>
526 _portalIndexesSQL = new HashMap<>();
527
528 }