Plan/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerTableResultTrans...

244 lines
10 KiB
Java

/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.transactions.results;
import com.djrapitops.plan.exceptions.database.DBOpException;
import com.djrapitops.plan.extension.implementation.ProviderInformation;
import com.djrapitops.plan.extension.implementation.providers.Parameters;
import com.djrapitops.plan.extension.table.Table;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.storage.database.queries.Query;
import com.djrapitops.plan.storage.database.queries.QueryStatement;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionPluginTable;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionTableProviderTable;
import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement;
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
import com.djrapitops.plan.storage.database.transactions.Executable;
import com.djrapitops.plan.storage.database.transactions.ThrowawayTransaction;
import org.apache.commons.lang3.StringUtils;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import java.util.UUID;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
import static com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionPlayerTableValueTable.*;
/**
* Transaction to store method result of a Table.
*
* @author AuroraLS3
*/
public class StorePlayerTableResultTransaction extends ThrowawayTransaction {
private final String pluginName;
private final ServerUUID serverUUID;
private final String providerName;
private final UUID playerUUID;
private final Table table;
public StorePlayerTableResultTransaction(String pluginName, ServerUUID serverUUID, String providerName, UUID playerUUID, Table table) {
this.pluginName = pluginName;
this.serverUUID = serverUUID;
this.providerName = providerName;
this.playerUUID = playerUUID;
this.table = table;
}
public StorePlayerTableResultTransaction(ProviderInformation information, Parameters parameters, Table value) {
this(information.getPluginName(), parameters.getServerUUID(), information.getName(), parameters.getPlayerUUID(), value);
}
@Override
protected void performOperations() {
execute(storeValue());
}
private Executable storeValue() {
return connection -> {
int maxColumnSize = table.getMaxColumnSize();
if (maxColumnSize == 0) {
return false;
}
Integer tableID = query(tableID());
List<Object[]> rows = table.getRows();
Integer oldRowCount = query(currentRowCount(tableID));
int newRowCount = rows.size();
if (oldRowCount < newRowCount) {
updateRows(tableID, oldRowCount, rows);
insertNewRows(tableID, oldRowCount, rows);
} else if (oldRowCount == newRowCount) {
// No need to delete or insert rows
updateRows(tableID, oldRowCount, rows);
} else {
// oldRowCount > newRowCount
updateRows(tableID, newRowCount, rows);
deleteOldRows(tableID, newRowCount);
}
return false;
};
}
private void deleteOldRows(Integer tableID, int afterRow) {
String sql = DELETE_FROM + TABLE_NAME +
WHERE + TABLE_ID + "=?" +
AND + USER_UUID + "=?" +
AND + TABLE_ROW + ">=?"; // Since row count is zero indexed and afterRow is size the value should be removed.
execute(new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, tableID);
statement.setString(2, playerUUID.toString());
statement.setInt(3, afterRow);
}
});
}
private void insertNewRows(Integer tableID, Integer afterRow, List<Object[]> rows) {
String sql = "INSERT INTO " + TABLE_NAME + '(' +
TABLE_ID + ',' +
USER_UUID + ',' +
VALUE_1 + ',' +
VALUE_2 + ',' +
VALUE_3 + ',' +
VALUE_4 + ',' +
TABLE_ROW +
") VALUES (?,?,?,?,?,?,?)";
execute(new ExecBatchStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
int maxColumnSize = Math.min(table.getMaxColumnSize(), 4); // Limit to maximum 4 columns, or how many column names there are.
for (int rowNumber = afterRow; rowNumber < rows.size(); rowNumber++) {
Object[] row = rows.get(rowNumber);
statement.setInt(1, tableID);
statement.setString(2, playerUUID.toString());
for (int i = 0; i < maxColumnSize; i++) {
Object value = row[i];
setStringOrNull(statement, 3 + i, value != null ? StringUtils.truncate(value.toString(), 250) : null);
}
// Rest are set null if not 4 columns wide.
for (int i = maxColumnSize; i < 4; i++) {
statement.setNull(3 + i, Types.VARCHAR);
}
statement.setInt(7, rowNumber);
statement.addBatch();
}
}
});
}
private void updateRows(Integer tableID, Integer untilRow, List<Object[]> rows) {
String sql = "UPDATE " + TABLE_NAME + " SET " +
VALUE_1 + "=?," +
VALUE_2 + "=?," +
VALUE_3 + "=?," +
VALUE_4 + "=?" +
WHERE + TABLE_ID + "=?" +
AND + USER_UUID + "=?" +
AND + TABLE_ROW + "=?";
execute(new ExecBatchStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
int maxColumnSize = Math.min(table.getMaxColumnSize(), 4); // Limit to maximum 4 columns, or how many column names there are.
for (int rowNumber = 0; rowNumber < untilRow; rowNumber++) {
Object[] row = rows.get(rowNumber);
for (int valueIndex = 0; valueIndex < maxColumnSize; valueIndex++) {
Object value = row[valueIndex];
setStringOrNull(statement, 1 + valueIndex, value != null ? StringUtils.truncate(value.toString(), 250) : null);
}
// Rest are set null if not 4 columns wide.
for (int valueIndex = maxColumnSize; valueIndex < 4; valueIndex++) {
statement.setNull(1 + valueIndex, Types.VARCHAR);
}
statement.setInt(5, tableID);
statement.setString(6, playerUUID.toString());
statement.setInt(7, rowNumber);
statement.addBatch();
}
}
});
}
private Query<Integer> currentRowCount(Integer tableID) {
String sql = SELECT + "COALESCE(MAX(" + TABLE_ROW + "), -1) as m" +
FROM + TABLE_NAME +
WHERE + TABLE_ID + "=?" +
AND + USER_UUID + "=?";
return new QueryStatement<>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, tableID);
statement.setString(2, playerUUID.toString());
}
@Override
public Integer processResults(ResultSet set) throws SQLException {
// add one to the row number, which is 0 indexed
return set.next() ? set.getInt("m") + 1 : 0;
}
};
}
private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException {
if (value != null) {
statement.setString(index, value);
} else {
statement.setNull(index, Types.VARCHAR);
}
}
private Query<Integer> tableID() {
String sql = SELECT + ExtensionTableProviderTable.ID +
FROM + ExtensionTableProviderTable.TABLE_NAME +
WHERE + ExtensionTableProviderTable.PROVIDER_NAME + "=?" +
AND + ExtensionTableProviderTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID +
" LIMIT 1";
return new QueryStatement<>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, providerName, pluginName, serverUUID);
}
@Override
public Integer processResults(ResultSet set) throws SQLException {
if (set.next()) {
int id = set.getInt(ExtensionTableProviderTable.ID);
if (!set.wasNull()) {
return id;
}
}
throw new DBOpException("Table Provider was not saved before storing results. Please report this issue. Extension method: " + pluginName + "#" + providerName);
}
};
}
}