From 7b41165af5515ad533b907a64fac4f6713fd7974 Mon Sep 17 00:00:00 2001 From: Aurora Lahtela <24460436+AuroraLS3@users.noreply.github.com> Date: Sun, 21 Aug 2022 12:19:52 +0300 Subject: [PATCH] Add /plan db migrate_to_online_uuids command --- Plan/common/build.gradle | 1 + .../djrapitops/plan/commands/PlanCommand.java | 14 ++- .../commands/subcommands/Confirmation.java | 2 +- .../subcommands/DatabaseCommands.java | 119 +++++++++++++++++- .../settings/locale/lang/CommandLang.java | 6 +- .../plan/settings/locale/lang/HelpLang.java | 3 +- .../queries/objects/BaseUserQueries.java | 14 ++- .../database/transactions/Transaction.java | 13 ++ .../commands/ChangeUserUUIDTransaction.java | 68 ++++++++++ .../commands/CombineUserTransaction.java | 87 +++++++++++++ .../database/transactions/patches/Patch.java | 11 -- Plan/fabric/build.gradle | 2 + Plan/plugin/build.gradle | 2 + 13 files changed, 316 insertions(+), 26 deletions(-) create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/ChangeUserUUIDTransaction.java create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/CombineUserTransaction.java diff --git a/Plan/common/build.gradle b/Plan/common/build.gradle index 6007c214a..a04c1660f 100644 --- a/Plan/common/build.gradle +++ b/Plan/common/build.gradle @@ -58,6 +58,7 @@ dependencies { shadow "org.eclipse.jetty:jetty-server:$jettyVersion" shadow "org.eclipse.jetty:jetty-alpn-java-server:$jettyVersion" shadow "org.eclipse.jetty.http2:http2-server:$jettyVersion" + shadow 'com.googlecode.json-simple:json-simple:1.1.1' // json simple used by UUIDFetcher // Swagger annotations implementation "jakarta.ws.rs:jakarta.ws.rs-api:3.1.0" diff --git a/Plan/common/src/main/java/com/djrapitops/plan/commands/PlanCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/PlanCommand.java index 6c15d5b5c..fa0ecc5d8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/commands/PlanCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/PlanCommand.java @@ -333,15 +333,27 @@ public class PlanCommand { .subcommand(removeCommand()) .subcommand(uninstalledCommand()) .subcommand(removeJoinAddressesCommand()) + .subcommand(onlineUuidMigration()) .requirePermission(Permissions.DATA_BASE) .description(locale.getString(HelpLang.DB)) .inDepthDescription(locale.getString(DeepHelpLang.DB)) .build(); } + private Subcommand onlineUuidMigration() { + return Subcommand.builder() + .aliases("migrate_to_online_uuids", "migratetoonlineuuids") + .requirePermission(Permissions.DATA_CLEAR) + .optionalArgument("--remove_offline", "Remove offline players if given") + .description(locale.getString(HelpLang.ONLINE_UUID_MIGRATION)) + .inDepthDescription("Moves and combines offline uuid data to online uuids where possible. Leaves offline-only players to database.") + .onCommand((sender, arguments) -> databaseCommands.onOnlineConversion(commandName, sender, arguments)) + .build(); + } + private Subcommand removeJoinAddressesCommand() { return Subcommand.builder() - .aliases("removejoinaddresses") + .aliases("remove_join_addresses", "removejoinaddresses") .requirePermission(Permissions.DATA_CLEAR) .requiredArgument(locale.getString(HelpLang.ARG_SERVER), locale.getString(HelpLang.DESC_ARG_SERVER_IDENTIFIER)) .description(locale.getString(HelpLang.JOIN_ADDRESS_REMOVAL)) diff --git a/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/Confirmation.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/Confirmation.java index a1e080072..ccf3c497c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/Confirmation.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/Confirmation.java @@ -39,7 +39,7 @@ public class Confirmation { ) { this.locale = locale; awaiting = Caffeine.newBuilder() - .expireAfterWrite(90, TimeUnit.SECONDS) + .expireAfterWrite(5, TimeUnit.MINUTES) .build(); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/DatabaseCommands.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/DatabaseCommands.java index f25a882f4..8624da57a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/DatabaseCommands.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/DatabaseCommands.java @@ -19,13 +19,16 @@ package com.djrapitops.plan.commands.subcommands; import com.djrapitops.plan.commands.use.Arguments; import com.djrapitops.plan.commands.use.CMDSender; import com.djrapitops.plan.commands.use.ColorScheme; +import com.djrapitops.plan.commands.use.MessageBuilder; import com.djrapitops.plan.delivery.formatting.Formatter; import com.djrapitops.plan.delivery.formatting.Formatters; import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.gathering.domain.BaseUser; import com.djrapitops.plan.identification.Identifiers; import com.djrapitops.plan.identification.Server; import com.djrapitops.plan.identification.ServerInfo; import com.djrapitops.plan.identification.ServerUUID; +import com.djrapitops.plan.processing.Processing; import com.djrapitops.plan.query.QuerySvc; import com.djrapitops.plan.settings.config.PlanConfig; import com.djrapitops.plan.settings.config.paths.DatabaseSettings; @@ -36,23 +39,24 @@ import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plan.storage.database.DBType; import com.djrapitops.plan.storage.database.Database; import com.djrapitops.plan.storage.database.SQLiteDB; +import com.djrapitops.plan.storage.database.queries.objects.BaseUserQueries; import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; import com.djrapitops.plan.storage.database.transactions.BackupCopyTransaction; -import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction; -import com.djrapitops.plan.storage.database.transactions.commands.RemovePlayerTransaction; -import com.djrapitops.plan.storage.database.transactions.commands.SetServerAsUninstalledTransaction; +import com.djrapitops.plan.storage.database.transactions.Transaction; +import com.djrapitops.plan.storage.database.transactions.commands.*; import com.djrapitops.plan.storage.database.transactions.patches.BadFabricJoinAddressValuePatch; import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plan.utilities.logging.ErrorContext; import com.djrapitops.plan.utilities.logging.ErrorLogger; +import net.playeranalytics.plugin.player.UUIDFetcher; import javax.inject.Inject; import javax.inject.Singleton; import java.io.File; import java.io.IOException; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; @Singleton public class DatabaseCommands { @@ -69,8 +73,10 @@ public class DatabaseCommands { private final Identifiers identifiers; private final PluginStatusCommands statusCommands; private final ErrorLogger errorLogger; + private final Processing processing; private final Formatter timestamp; + private final Formatter clock; @Inject public DatabaseCommands( @@ -86,7 +92,8 @@ public class DatabaseCommands { Formatters formatters, Identifiers identifiers, PluginStatusCommands statusCommands, - ErrorLogger errorLogger + ErrorLogger errorLogger, + Processing processing ) { this.locale = locale; this.confirmation = confirmation; @@ -102,6 +109,8 @@ public class DatabaseCommands { this.errorLogger = errorLogger; this.timestamp = formatters.iso8601NoClockLong(); + clock = formatters.clockLong(); + this.processing = processing; } public void onBackup(CMDSender sender, Arguments arguments) { @@ -472,4 +481,102 @@ public class DatabaseCommands { } statusCommands.onReload(sender); } + + public void onOnlineConversion(String mainCommand, CMDSender sender, Arguments arguments) { + boolean removeOfflinePlayers = arguments.get(0) + .map("--remove_offline"::equals) + .orElse(false); + sender.send(locale.getString(CommandLang.PROGRESS_PREPARING)); + processing.submitNonCritical(() -> { + Map baseUsersByUUID = dbSystem.getDatabase().query(BaseUserQueries.fetchAllBaseUsersByUUID()); + List playerNames = baseUsersByUUID.values().stream().map(BaseUser::getName).collect(Collectors.toList()); + sender.send("Performing lookup for " + playerNames.size() + " uuids from Mojang.."); + sender.send("Preparation estimated complete at: " + clock.apply(System.currentTimeMillis() + playerNames.size() * 100) + " (due to request rate limiting)"); + Map onlineUUIDsOfPlayers = getUUIDViaUUIDFetcher(playerNames); + + if (onlineUUIDsOfPlayers.isEmpty()) { + sender.send(locale.getString(CommandLang.PROGRESS_FAIL, "Did not get any UUIDs from Mojang.")); + return; + } + + int totalProfiles = baseUsersByUUID.size(); + int offlineOnlyUsers = 0; + int combine = 0; + int move = 0; + + List transactions = new ArrayList<>(); + + for (BaseUser user : baseUsersByUUID.values()) { + String playerName = user.getName(); + UUID recordedUUID = user.getUuid(); + UUID actualUUID = onlineUUIDsOfPlayers.get(playerName); + + if (actualUUID == null) { + offlineOnlyUsers++; + if (removeOfflinePlayers) transactions.add(new RemovePlayerTransaction(recordedUUID)); + continue; + } + if (recordedUUID == actualUUID) { + continue; + } + BaseUser alreadyExistingProfile = baseUsersByUUID.get(actualUUID); + if (alreadyExistingProfile == null) { + move++; + transactions.add(new ChangeUserUUIDTransaction(recordedUUID, actualUUID)); + } else { + combine++; + transactions.add(new CombineUserTransaction(recordedUUID, actualUUID)); + } + } + + MessageBuilder messageBuilder = sender.buildMessage() + .addPart(colors.getMainColor() + "Moving to online-only UUIDs (irreversible):").newLine() + .addPart(colors.getSecondaryColor() + " Total players in database: " + totalProfiles).newLine() + .addPart(colors.getSecondaryColor() + (removeOfflinePlayers ? "Removing (no online UUID): " : " Offline only (no online UUID): ") + offlineOnlyUsers).newLine() + .addPart(colors.getSecondaryColor() + " Moving to new UUID: " + move).newLine() + .addPart(colors.getSecondaryColor() + " Combining offline and online profiles: " + combine).newLine() + .newLine() + .addPart(colors.getSecondaryColor() + " Estimated online UUID players in database after: " + (totalProfiles - combine - offlineOnlyUsers) + (removeOfflinePlayers ? "" : " (+" + offlineOnlyUsers + " offline)")).newLine() + .addPart(colors.getTertiaryColor() + locale.getString(CommandLang.CONFIRM)); + if (sender.supportsChatEvents()) { + messageBuilder + .addPart("§2§l[\u2714]").command("/" + mainCommand + " accept").hover(locale.getString(CommandLang.CONFIRM_ACCEPT)) + .addPart(" ") + .addPart("§4§l[\u2718]").command("/" + mainCommand + " cancel").hover(locale.getString(CommandLang.CONFIRM_DENY)) + .send(); + } else { + messageBuilder + .addPart(colors.getTertiaryColor() + locale.getString(CommandLang.CONFIRM)).addPart("§a/" + mainCommand + " accept") + .addPart(" ") + .addPart("§c/" + mainCommand + " cancel") + .send(); + } + + confirmation.confirm(sender, choice -> { + if (Boolean.TRUE.equals(choice)) { + transactions.forEach(dbSystem.getDatabase()::executeTransaction); + dbSystem.getDatabase().executeTransaction(new Transaction() { + @Override + protected void performOperations() { + sender.send(locale.getString(CommandLang.PROGRESS_SUCCESS)); + } + }); + } else { + sender.send(colors.getMainColor() + locale.getString(CommandLang.CONFIRM_CANCELLED_DATA)); + } + }); + + }); + } + + private Map getUUIDViaUUIDFetcher(List playerNames) { + try { + return new UUIDFetcher(playerNames).call(); + } catch (Exception | NoClassDefFoundError failure) { + errorLogger.error(failure, ErrorContext.builder() + .related("Migrating offline uuids to online uuids") + .build()); + return new HashMap<>(); + } + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/CommandLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/CommandLang.java index 16e4e8261..711aa5b63 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/CommandLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/CommandLang.java @@ -118,6 +118,7 @@ public enum CommandLang implements Lang { HOTSWAP_REMINDER("command.database.manage.hotswap", "Manage - Remind HotSwap", "§eRemember to swap to the new database (/plan db hotswap ${0}) & reload the plugin."), PROGRESS_START("command.database.manage.start", "Manage - Start", "> §2Processing data.."), PROGRESS("command.database.manage.progress", "Manage - Progress", "${0} / ${1} processed.."), + PROGRESS_PREPARING("command.database.manage.preparing", "Manage - preparing", "Preparing.."), PROGRESS_SUCCESS("command.database.manage.success", "Manage - Success", "> §aSuccess!"), PROGRESS_FAIL("command.database.manage.fail", "Manage - Fail", "> §cSomething went wrong: ${0}"), CONFIRMATION("command.database.manage.confirm", "Manage - Fail, Confirmation", "> §cAdd '-a' argument to confirm execution: ${0}"), @@ -131,7 +132,8 @@ public enum CommandLang implements Lang { FAIL_IMPORTER_NOT_FOUND("command.general.failNoImporter", "Manage - Fail No Importer", "§eImporter '${0}' doesn't exist"), FAIL_EXPORTER_NOT_FOUND("command.general.failNoExporter", "Manage - Fail No Exporter", "§eExporter '${0}' doesn't exist"), NO_SERVER("command.database.manage.failNoServer", "Manage - Fail No Server", "No server found with given parameters."), - UNINSTALLING_SAME_SERVER("command.database.manage.failSameServer", "Manage - Fail Same server", "Can not mark this server as uninstalled (You are on it)"); + UNINSTALLING_SAME_SERVER("command.database.manage.failSameServer", "Manage - Fail Same server", "Can not mark this server as uninstalled (You are on it)"), + ; private final String key; private final String identifier; @@ -149,7 +151,7 @@ public enum CommandLang implements Lang { } @Override - public String getKey() { return key; } + public String getKey() {return key;} @Override public String getDefault() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HelpLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HelpLang.java index 11b4d8587..5a002eb96 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HelpLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HelpLang.java @@ -71,7 +71,8 @@ public enum HelpLang implements Lang { IMPORT("command.help.import.description", "Command Help - /plan import", "Import data"), JSON("command.help.json.description", "Command Help - /plan json", "View json of Player's raw data."), LOGOUT("command.help.logout.description", "Command Help - /plan logout", "Log out other users from the panel."), - JOIN_ADDRESS_REMOVAL("command.help.removejoinaddresses.description", "Command Help - /plan db removejoinaddresses", "Remove join addresses of a specified server"); + JOIN_ADDRESS_REMOVAL("command.help.removejoinaddresses.description", "Command Help - /plan db removejoinaddresses", "Remove join addresses of a specified server"), + ONLINE_UUID_MIGRATION("command.help.migrateToOnlineUuids.description", "Command Help - /plan db migratetoonlineuuids", "Migrate offline uuid data to online uuids"); private final String identifier; private final String key; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/BaseUserQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/BaseUserQueries.java index 871371235..c99580492 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/BaseUserQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/BaseUserQueries.java @@ -26,10 +26,7 @@ import org.apache.commons.text.TextStringBuilder; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.Collection; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; +import java.util.*; import static com.djrapitops.plan.storage.database.sql.building.Sql.*; @@ -57,6 +54,15 @@ public class BaseUserQueries { return db -> db.queryList(sql, BaseUserQueries::extractBaseUser); } + public static Query> fetchAllBaseUsersByUUID() { + String sql = Select.all(UsersTable.TABLE_NAME).toString(); + + return db -> db.queryMap(sql, (results, map) -> { + BaseUser baseUser = extractBaseUser(results); + map.put(baseUser.getUuid(), baseUser); + }, HashMap::new); + } + private static BaseUser extractBaseUser(ResultSet set) throws SQLException { UUID playerUUID = UUID.fromString(set.getString(UsersTable.USER_UUID)); String name = set.getString(UsersTable.USER_NAME); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/Transaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/Transaction.java index 52de86c61..6afe11725 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/Transaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/Transaction.java @@ -25,6 +25,8 @@ import com.djrapitops.plan.storage.database.SQLDB; import com.djrapitops.plan.storage.database.queries.Query; import com.djrapitops.plan.storage.database.queries.QueryAPIQuery; import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.queries.schema.MySQLSchemaQueries; +import com.djrapitops.plan.storage.database.queries.schema.SQLiteSchemaQueries; import com.djrapitops.plan.storage.database.transactions.patches.Patch; import com.djrapitops.plan.utilities.logging.ErrorContext; import net.playeranalytics.plugin.scheduling.TimeAmount; @@ -274,4 +276,15 @@ public abstract class Transaction { String simpleName = getClass().getSimpleName(); return simpleName.isEmpty() ? getClass().getName() : simpleName; } + + protected boolean hasTable(String tableName) { + switch (dbType) { + case SQLITE: + return query(SQLiteSchemaQueries.doesTableExist(tableName)); + case MYSQL: + return query(MySQLSchemaQueries.doesTableExist(tableName)); + default: + throw new IllegalStateException("Unsupported Database Type: " + dbType.getName()); + } + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/ChangeUserUUIDTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/ChangeUserUUIDTransaction.java new file mode 100644 index 000000000..d85f05a44 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/ChangeUserUUIDTransaction.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.transactions.commands; + +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.UUID; + +import static com.djrapitops.plan.storage.database.sql.building.Sql.WHERE; + +/** + * Intends to correct UUID of a user. + * + * @author AuroraLS3 + */ +public class ChangeUserUUIDTransaction extends Transaction { + + protected final UUID oldUUID; + protected final UUID newUUID; + + public ChangeUserUUIDTransaction(UUID oldUUID, UUID newUUID) { + this.oldUUID = oldUUID; + this.newUUID = newUUID; + } + + @Override + protected void performOperations() { + execute(updateUUID(ExtensionGroupsTable.TABLE_NAME, ExtensionGroupsTable.USER_UUID)); + execute(updateUUID(ExtensionPlayerTableValueTable.TABLE_NAME, ExtensionPlayerTableValueTable.USER_UUID)); + execute(updateUUID(NicknamesTable.TABLE_NAME, NicknamesTable.USER_UUID)); + execute(updateUUID(UsersTable.TABLE_NAME, UsersTable.USER_UUID)); + execute(updateUUID(KillsTable.TABLE_NAME, KillsTable.VICTIM_UUID)); + execute(updateUUID(KillsTable.TABLE_NAME, KillsTable.KILLER_UUID)); + + if (hasTable("plan_platforms")) execute(updateUUID("plan_platforms", "uuid")); + if (hasTable("plan_tebex_payments")) execute(updateUUID("plan_tebex_payments", "uuid")); + if (hasTable("plan_version_protocol")) execute(updateUUID("plan_version_protocol", "uuid")); + } + + private Executable updateUUID(String tableName, String columnName) { + return new ExecStatement("UPDATE " + tableName + " SET " + columnName + "=?" + WHERE + columnName + "=?") { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, newUUID.toString()); + statement.setString(2, oldUUID.toString()); + } + }; + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/CombineUserTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/CombineUserTransaction.java new file mode 100644 index 000000000..cd3cad9cb --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/CombineUserTransaction.java @@ -0,0 +1,87 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.transactions.commands; + +import com.djrapitops.plan.storage.database.queries.objects.BaseUserQueries; +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Optional; +import java.util.UUID; + +import static com.djrapitops.plan.storage.database.sql.building.Sql.*; + +/** + * Intends to correct UUID of a user. + * + * @author AuroraLS3 + */ +public class CombineUserTransaction extends ChangeUserUUIDTransaction { + + public CombineUserTransaction(UUID oldUUID, UUID newUUID) { + super(oldUUID, newUUID); + } + + @Override + protected void performOperations() { + Optional foundOldId = query(BaseUserQueries.fetchUserId(oldUUID)); + Optional foundNewId = query(BaseUserQueries.fetchUserId(newUUID)); + if (foundOldId.isEmpty() || foundNewId.isEmpty()) return; + + Integer oldId = foundOldId.get(); + Integer newId = foundNewId.get(); + + execute(updateUserId(GeoInfoTable.TABLE_NAME, GeoInfoTable.USER_ID, oldId, newId)); + execute(updateUserId(PingTable.TABLE_NAME, PingTable.USER_ID, oldId, newId)); + execute(updateUserId(SessionsTable.TABLE_NAME, SessionsTable.USER_ID, oldId, newId)); + execute(updateUserId(WorldTimesTable.TABLE_NAME, WorldTimesTable.USER_ID, oldId, newId)); + + execute(updateUserInfo(newId, oldId)); + execute(DELETE_FROM + UserInfoTable.TABLE_NAME + WHERE + UserInfoTable.USER_ID + "=" + oldId); + + super.performOperations(); // Change UUID fields to match where user_id is not used + } + + private Executable updateUserInfo(Integer newId, Integer oldId) { + String sql = "UPDATE " + UserInfoTable.TABLE_NAME + + " SET " + UserInfoTable.USER_ID + "=?" + + WHERE + UserInfoTable.USER_ID + "=?" + + AND + UserInfoTable.SERVER_ID + " NOT IN (" + + SELECT + UserInfoTable.SERVER_ID + FROM + UserInfoTable.TABLE_NAME + WHERE + UserInfoTable.USER_ID + "=?)"; + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setInt(1, newId); + statement.setInt(2, oldId); + statement.setInt(3, newId); + } + }; + } + + private Executable updateUserId(String tableName, String columnName, Integer oldId, Integer newId) { + return new ExecStatement("UPDATE " + tableName + " SET " + columnName + "=?" + WHERE + columnName + "=?") { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setInt(1, newId); + statement.setInt(2, oldId); + } + }; + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/Patch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/Patch.java index e77935f2f..34d3af401 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/Patch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/Patch.java @@ -72,17 +72,6 @@ public abstract class Patch extends OperationCriticalTransaction { execute("SET FOREIGN_KEY_CHECKS=0"); } - protected boolean hasTable(String tableName) { - switch (dbType) { - case SQLITE: - return query(SQLiteSchemaQueries.doesTableExist(tableName)); - case MYSQL: - return query(MySQLSchemaQueries.doesTableExist(tableName)); - default: - throw new IllegalStateException("Unsupported Database Type: " + dbType.getName()); - } - } - protected boolean hasColumn(String tableName, String columnName) { switch (dbType) { case MYSQL: diff --git a/Plan/fabric/build.gradle b/Plan/fabric/build.gradle index c54fb7878..3ca9ad722 100644 --- a/Plan/fabric/build.gradle +++ b/Plan/fabric/build.gradle @@ -103,6 +103,8 @@ shadowJar { relocate 'org.slf4j', 'plan.org.slf4j' + relocate 'org.json.simple', 'plan.org.json.simple' + mergeServiceFiles() } diff --git a/Plan/plugin/build.gradle b/Plan/plugin/build.gradle index 9397e52bc..92e215daf 100644 --- a/Plan/plugin/build.gradle +++ b/Plan/plugin/build.gradle @@ -88,6 +88,8 @@ shadowJar { relocate 'jakarta.servlet', 'plan.jakarta.servlet' relocate 'javax.servlet', 'plan.javax.servlet' + relocate 'org.json.simple', 'plan.org.json.simple' + destinationDirectory.set(file("$rootDir/builds/")) archiveBaseName.set('Plan') archiveClassifier.set('')