All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.liferay.portal.upgrade.util.Table Maven / Gradle / Ivy

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

package com.liferay.portal.upgrade.util;

import com.liferay.petra.string.StringBundler;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.dao.jdbc.postgresql.PostgreSQLJDBCUtil;
import com.liferay.portal.kernel.dao.db.DB;
import com.liferay.portal.kernel.dao.db.DBManagerUtil;
import com.liferay.portal.kernel.dao.db.DBType;
import com.liferay.portal.kernel.dao.jdbc.AutoBatchPreparedStatementUtil;
import com.liferay.portal.kernel.dao.jdbc.DataAccess;
import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
import com.liferay.portal.kernel.io.unsync.UnsyncBufferedWriter;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.upgrade.StagnantRowException;
import com.liferay.portal.kernel.upgrade.UpgradeException;
import com.liferay.portal.kernel.util.Base64;
import com.liferay.portal.kernel.util.DateUtil;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.SystemProperties;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.uuid.PortalUUIDUtil;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.Reader;

import java.math.BigDecimal;

import java.nio.file.Files;
import java.nio.file.Paths;

import java.sql.Clob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;

import java.text.DateFormat;

import java.util.Date;

import org.apache.commons.lang.time.StopWatch;

/**
 * @author Alexander Chow
 * @author Brian Wing Shun Chan
 * @author Raymond Augé
 */
public class Table {

	public Table(String tableName) {
		_tableName = tableName;
	}

	public Table(String tableName, Object[][] columns) {
		_tableName = tableName;

		setColumns(columns);
	}

	public void appendColumn(StringBuilder sb, Object value, boolean last)
		throws Exception {

		if (value == null) {
			throw new UpgradeException(
				StringBundler.concat(
					"Nulls should never be inserted into the database. ",
					"Attempted to append column to ", sb.toString(), "."));
		}
		else if (value instanceof byte[]) {
			sb.append(Base64.encode((byte[])value));
		}
		else if (value instanceof Clob || value instanceof String) {
			value = StringUtil.replace(
				(String)value, _SAFE_TABLE_CHARS[0], _SAFE_TABLE_CHARS[1]);

			sb.append(value);
		}
		else if (value instanceof Date) {
			DateFormat df = DateUtil.getISOFormat();

			sb.append(df.format(value));
		}
		else {
			sb.append(value);
		}

		sb.append(StringPool.COMMA);

		if (last) {
			sb.append(StringPool.NEW_LINE);
		}
	}

	public void appendColumn(
			StringBuilder sb, ResultSet resultSet, String name, Integer type,
			boolean last)
		throws Exception {

		Object value = null;

		try {
			value = getValue(resultSet, name, type);
		}
		catch (SQLException sqlException) {
			if (_log.isDebugEnabled()) {
				_log.debug(sqlException);
			}

			if (name.equals("uuid_")) {
				sb.append(PortalUUIDUtil.generate());
			}

			sb.append(StringPool.COMMA);

			if (last) {
				sb.append(StringPool.NEW_LINE);
			}

			return;
		}

		appendColumn(sb, value, last);
	}

	public void generateTempFile() throws Exception {
		try (Connection connection = DataAccess.getConnection()) {
			generateTempFile(connection);
		}
	}

	public void generateTempFile(Connection connection) throws Exception {
		boolean empty = true;

		String tempFileName = String.valueOf(
			Files.createTempFile(
				Paths.get(SystemProperties.get(SystemProperties.TMP_DIR)),
				"temp-db-" + _tableName + "-", null));

		StopWatch stopWatch = new StopWatch();

		stopWatch.start();

		if (_log.isInfoEnabled()) {
			_log.info(
				StringBundler.concat(
					"Starting backup of ", _tableName, " to ", tempFileName));
		}

		try (UnsyncBufferedWriter unsyncBufferedWriter =
				new UnsyncBufferedWriter(new FileWriter(tempFileName));
			PreparedStatement preparedStatement = getSelectPreparedStatement(
				connection);
			ResultSet resultSet = preparedStatement.executeQuery()) {

			while (resultSet.next()) {
				String data = null;

				try {
					data = getExportedData(resultSet);

					unsyncBufferedWriter.write(data);

					_totalRows++;

					empty = false;
				}
				catch (StagnantRowException stagnantRowException) {
					if (_log.isWarnEnabled()) {
						_log.warn(
							StringBundler.concat(
								"Skipping stagnant data in ", _tableName, ": ",
								stagnantRowException.getMessage()));
					}
				}
			}

			if (_log.isInfoEnabled()) {
				_log.info(
					StringBundler.concat(
						"Finished backup of ", _tableName, " to ", tempFileName,
						" in ", stopWatch.getTime(), " ms"));
			}
		}
		catch (Exception exception) {
			FileUtil.delete(tempFileName);

			throw exception;
		}

		if (!empty) {
			_tempFileName = tempFileName;

			return;
		}

		FileUtil.delete(tempFileName);
	}

	public Object[][] getColumns() {
		return _columns;
	}

	public String getCreateSQL() throws Exception {
		return _createSQL;
	}

	public String getDeleteSQL() throws Exception {
		return "DELETE FROM " + _tableName;
	}

	public String getExportedData(ResultSet resultSet) throws Exception {
		StringBuilder sb = new StringBuilder();

		Object[][] columns = getColumns();

		for (int i = 0; i < columns.length; i++) {
			boolean last = false;

			if ((i + 1) == columns.length) {
				last = true;
			}

			appendColumn(
				sb, resultSet, (String)columns[i][0], (Integer)columns[i][1],
				last);
		}

		return sb.toString();
	}

	public String getInsertSQL() throws Exception {
		String sql = "INSERT INTO " + getInsertTableName() + " (";

		for (int i = 0; i < _order.length; i++) {
			int pos = _order[i];

			sql += _columns[pos][0];

			if ((i + 1) < _columns.length) {
				sql += ", ";
			}
			else {
				sql += ") VALUES (";
			}
		}

		for (int i = 0; i < _columns.length; i++) {
			sql += "?";

			if ((i + 1) < _columns.length) {
				sql += ", ";
			}
			else {
				sql += ")";
			}
		}

		return sql;
	}

	public String getInsertTableName() throws Exception {
		String createSQL = getCreateSQL();

		if (Validator.isNotNull(createSQL)) {
			String createSQLLowerCase = StringUtil.toLowerCase(createSQL);

			int x = createSQLLowerCase.indexOf("create table ");

			if (x == -1) {
				return _tableName;
			}

			x += 13;

			int y = createSQL.indexOf(" ", x);

			return StringUtil.trim(createSQL.substring(x, y));
		}

		return _tableName;
	}

	public int[] getOrder() {
		return _order;
	}

	public PreparedStatement getSelectPreparedStatement(Connection connection)
		throws Exception {

		return connection.prepareStatement(getSelectSQL());
	}

	public String getSelectSQL() throws Exception {
		if (_selectSQL == null) {
			/*String sql = "select ";

			for (int i = 0; i < _columns.length; i++) {
				sql += _columns[i][0];

				if ((i + 1) < _columns.length) {
					sql += ", ";
				}
				else {
					sql += " from " + _tableName;
				}
			}

			return sql;*/

			return "select * from " + _tableName;
		}

		return _selectSQL;
	}

	public String getTableName() {
		return _tableName;
	}

	public String getTempFileName() {
		return _tempFileName;
	}

	public long getTotalRows() {
		return _totalRows;
	}

	/**
	 * @see com.liferay.object.service.impl.ObjectEntryLocalServiceImpl#_getValue
	 */
	public Object getValue(ResultSet resultSet, String name, Integer type)
		throws Exception {

		Object value = null;

		int t = type.intValue();

		if (t == Types.BIGINT) {
			try {
				value = GetterUtil.getLong(resultSet.getLong(name));
			}
			catch (SQLException sqlException) {
				if (_log.isDebugEnabled()) {
					_log.debug(sqlException);
				}

				value = GetterUtil.getLong(resultSet.getString(name));
			}
		}
		else if (t == Types.BIT) {
			value = GetterUtil.getBoolean(resultSet.getBoolean(name));
		}
		else if ((t == Types.BLOB) || (t == Types.LONGVARBINARY)) {
			DB db = DBManagerUtil.getDB();

			DBType dbType = db.getDBType();

			if (dbType.equals(DBType.POSTGRESQL) &&
				PostgreSQLJDBCUtil.isPGStatement(resultSet.getStatement())) {

				value = PostgreSQLJDBCUtil.getLargeObject(resultSet, name);
			}
			else {
				value = resultSet.getBytes(name);
			}

			if (value == null) {
				value = new byte[0];
			}
		}
		else if (t == Types.BOOLEAN) {
			value = GetterUtil.getBoolean(resultSet.getBoolean(name));
		}
		else if (t == Types.CLOB) {
			try {
				Clob clob = resultSet.getClob(name);

				if (clob == null) {
					value = StringPool.BLANK;
				}
				else {
					try (Reader reader = clob.getCharacterStream();
						UnsyncBufferedReader unsyncBufferedReader =
							new UnsyncBufferedReader(reader)) {

						StringBundler sb = new StringBundler();

						String line = null;

						while ((line = unsyncBufferedReader.readLine()) !=
									null) {

							if (sb.length() != 0) {
								sb.append(_SAFE_TABLE_NEWLINE_CHARACTER);
							}

							sb.append(line);
						}

						value = sb.toString();
					}
				}
			}
			catch (Exception exception) {
				if (_log.isDebugEnabled()) {
					_log.debug(exception);
				}

				// If the database doesn't allow CLOB types for the column
				// value, then try retrieving it as a String

				value = GetterUtil.getString(resultSet.getString(name));
			}
		}
		else if (t == Types.DECIMAL) {
			try {
				value = resultSet.getBigDecimal(name);
			}
			catch (SQLException sqlException) {
				if (_log.isDebugEnabled()) {
					_log.debug(sqlException);
				}

				value = resultSet.getString(name);
			}

			value = GetterUtil.get(value, BigDecimal.ZERO);
		}
		else if (t == Types.DOUBLE) {
			value = GetterUtil.getDouble(resultSet.getDouble(name));
		}
		else if (t == Types.FLOAT) {
			value = GetterUtil.getFloat(resultSet.getFloat(name));
		}
		else if (t == Types.INTEGER) {
			value = GetterUtil.getInteger(resultSet.getInt(name));
		}
		else if (t == Types.LONGVARCHAR) {
			value = GetterUtil.getString(resultSet.getString(name));
		}
		else if (t == Types.NUMERIC) {
			value = GetterUtil.getLong(resultSet.getLong(name));
		}
		else if (t == Types.SMALLINT) {
			value = GetterUtil.getShort(resultSet.getShort(name));
		}
		else if (t == Types.TIMESTAMP) {
			try {
				value = resultSet.getTimestamp(name);
			}
			catch (Exception exception) {
				if (_log.isDebugEnabled()) {
					_log.debug(exception);
				}
			}

			if (value == null) {
				value = StringPool.NULL;
			}
		}
		else if (t == Types.TINYINT) {
			value = GetterUtil.getShort(resultSet.getShort(name));
		}
		else if (t == Types.VARCHAR) {
			value = GetterUtil.getString(resultSet.getString(name));
		}
		else {
			throw new UpgradeException(
				"Upgrade code using unsupported class type " + type);
		}

		return value;
	}

	public void populateTable() throws Exception {
		try (Connection connection = DataAccess.getConnection()) {
			populateTable(connection);
		}
	}

	public void populateTable(Connection connection) throws Exception {
		if (_tempFileName == null) {
			return;
		}

		try (PreparedStatement preparedStatement =
				AutoBatchPreparedStatementUtil.autoBatch(
					connection, getInsertSQL());
			UnsyncBufferedReader unsyncBufferedReader =
				new UnsyncBufferedReader(new FileReader(_tempFileName))) {

			String line = null;

			while ((line = unsyncBufferedReader.readLine()) != null) {
				String[] values = StringUtil.split(line);

				Object[][] columns = getColumns();

				if (values.length != columns.length) {
					throw new UpgradeException(
						StringBundler.concat(
							"Column lengths differ between temp file and ",
							"schema. Attempted to insert row ", line, "."));
				}

				int[] order = getOrder();

				for (int i = 0; i < order.length; i++) {
					int pos = order[i];

					setColumn(
						preparedStatement, i, (Integer)columns[pos][1],
						values[pos]);
				}

				preparedStatement.addBatch();
			}

			preparedStatement.executeBatch();
		}

		if (_log.isDebugEnabled()) {
			_log.debug(getTableName() + " table populated with data");
		}
	}

	public void populateTableRows(
			PreparedStatement preparedStatement, boolean batch)
		throws Exception {

		if (_log.isDebugEnabled()) {
			_log.debug("Updating rows for " + getTableName());
		}

		if (batch) {
			preparedStatement.executeBatch();
		}
		else {
			preparedStatement.executeUpdate();
		}

		preparedStatement.close();
	}

	/**
	 * @see com.liferay.object.service.impl.ObjectEntryLocalServiceImpl#_setColumn
	 */
	public void setColumn(
			PreparedStatement preparedStatement, int index, Integer type,
			String value)
		throws Exception {

		index++;

		int t = type.intValue();

		if (t == Types.BIGINT) {
			preparedStatement.setLong(index, GetterUtil.getLong(value));
		}
		else if ((t == Types.BLOB) || (t == Types.LONGVARBINARY)) {
			byte[] valueBytes = Base64.decode(value);

			if (PostgreSQLJDBCUtil.isPGStatement(preparedStatement)) {
				PostgreSQLJDBCUtil.setLargeObject(
					preparedStatement, index, valueBytes);
			}
			else {
				preparedStatement.setBytes(index, valueBytes);
			}
		}
		else if (t == Types.BOOLEAN) {
			preparedStatement.setBoolean(index, GetterUtil.getBoolean(value));
		}
		else if ((t == Types.CLOB) || (t == Types.LONGVARCHAR) ||
				 (t == Types.VARCHAR)) {

			value = StringUtil.replace(
				value, _SAFE_TABLE_CHARS[1], _SAFE_TABLE_CHARS[0]);

			preparedStatement.setString(index, value);
		}
		else if (t == Types.DECIMAL) {
			preparedStatement.setBigDecimal(
				index, (BigDecimal)GetterUtil.get(value, BigDecimal.ZERO));
		}
		else if (t == Types.DOUBLE) {
			preparedStatement.setDouble(index, GetterUtil.getDouble(value));
		}
		else if (t == Types.FLOAT) {
			preparedStatement.setFloat(index, GetterUtil.getFloat(value));
		}
		else if (t == Types.INTEGER) {
			preparedStatement.setInt(index, GetterUtil.getInteger(value));
		}
		else if (t == Types.SMALLINT) {
			preparedStatement.setShort(index, GetterUtil.getShort(value));
		}
		else if (t == Types.TIMESTAMP) {
			if (StringPool.NULL.equals(value)) {
				preparedStatement.setTimestamp(index, null);
			}
			else {
				DateFormat dateFormat = DateUtil.getISOFormat();

				Date date = dateFormat.parse(value);

				preparedStatement.setTimestamp(
					index, new Timestamp(date.getTime()));
			}
		}
		else if (t == Types.TINYINT) {
			preparedStatement.setShort(index, GetterUtil.getShort(value));
		}
		else {
			throw new UpgradeException(
				"Upgrade code using unsupported class type " + type);
		}
	}

	public void setColumns(Object[][] columns) {
		_columns = columns;

		// LEP-7331

		_order = new int[_columns.length];

		int clobCount = 0;

		for (int i = 0; i < _columns.length; ++i) {
			Integer type = (Integer)columns[i][1];

			if (type.intValue() == Types.CLOB) {
				clobCount++;

				int pos = _columns.length - clobCount;

				_order[pos] = i;
			}
			else {
				int pos = i - clobCount;

				_order[pos] = i;
			}
		}
	}

	public void setCreateSQL(String createSQL) throws Exception {
		_createSQL = createSQL;
	}

	public void setSelectSQL(String selectSQL) throws Exception {
		_selectSQL = selectSQL;
	}

	public void updateColumnValue(
		String columnName, String oldValue, String newValue) {

		String sql = StringBundler.concat(
			"update ", _tableName, " set ", columnName, " = ? where ",
			columnName, " = ?");

		try (Connection connection = DataAccess.getConnection();
			PreparedStatement preparedStatement = connection.prepareStatement(
				sql)) {

			preparedStatement.setString(1, newValue);
			preparedStatement.setString(2, oldValue);

			preparedStatement.executeUpdate();
		}
		catch (SQLException sqlException) {
			_log.error(sqlException);

			throw new RuntimeException(
				"Unable to execute " + sql, sqlException);
		}
	}

	private static final String[][] _SAFE_TABLE_CHARS = {
		{StringPool.COMMA, StringPool.NEW_LINE, StringPool.RETURN},
		{
			Table._SAFE_TABLE_COMMA_CHARACTER,
			Table._SAFE_TABLE_NEWLINE_CHARACTER,
			Table._SAFE_TABLE_RETURN_CHARACTER
		}
	};

	private static final String _SAFE_TABLE_COMMA_CHARACTER =
		"_SAFE_TABLE_COMMA_CHARACTER_";

	private static final String _SAFE_TABLE_NEWLINE_CHARACTER =
		"_SAFE_TABLE_NEWLINE_CHARACTER_";

	private static final String _SAFE_TABLE_RETURN_CHARACTER =
		"_SAFE_TABLE_RETURN_CHARACTER_";

	private static final Log _log = LogFactoryUtil.getLog(Table.class);

	private Object[][] _columns;
	private String _createSQL;
	private int[] _order;
	private String _selectSQL;
	private final String _tableName;
	private String _tempFileName;
	private long _totalRows;

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy