mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2025-01-04 06:57:41 +01:00
- Move MySQL migrations into a separate class - Add migration for last IP column to be nullable if it has a not null constraint without a default value
This commit is contained in:
parent
2c6181d150
commit
e3dd719b0e
@ -20,7 +20,6 @@ import java.sql.PreparedStatement;
|
|||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.sql.Types;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -193,17 +192,19 @@ public class MySQL implements DataSource {
|
|||||||
if (isColumnMissing(md, col.LAST_IP)) {
|
if (isColumnMissing(md, col.LAST_IP)) {
|
||||||
st.executeUpdate("ALTER TABLE " + tableName
|
st.executeUpdate("ALTER TABLE " + tableName
|
||||||
+ " ADD COLUMN " + col.LAST_IP + " VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin;");
|
+ " ADD COLUMN " + col.LAST_IP + " VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin;");
|
||||||
|
} else {
|
||||||
|
MySqlMigrater.migrateLastIpColumn(st, md, tableName, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isColumnMissing(md, col.LAST_LOGIN)) {
|
if (isColumnMissing(md, col.LAST_LOGIN)) {
|
||||||
st.executeUpdate("ALTER TABLE " + tableName
|
st.executeUpdate("ALTER TABLE " + tableName
|
||||||
+ " ADD COLUMN " + col.LAST_LOGIN + " BIGINT;");
|
+ " ADD COLUMN " + col.LAST_LOGIN + " BIGINT;");
|
||||||
} else {
|
} else {
|
||||||
migrateLastLoginColumn(st, md);
|
MySqlMigrater.migrateLastLoginColumn(st, md, tableName, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isColumnMissing(md, col.REGISTRATION_DATE)) {
|
if (isColumnMissing(md, col.REGISTRATION_DATE)) {
|
||||||
addRegistrationDateColumn(st);
|
MySqlMigrater.addRegistrationDateColumn(st, tableName, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isColumnMissing(md, col.REGISTRATION_IP)) {
|
if (isColumnMissing(md, col.REGISTRATION_IP)) {
|
||||||
@ -730,103 +731,4 @@ public class MySQL implements DataSource {
|
|||||||
.locPitch(row.getFloat(col.LASTLOC_PITCH))
|
.locPitch(row.getFloat(col.LASTLOC_PITCH))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the last login column has a type that needs to be migrated.
|
|
||||||
*
|
|
||||||
* @param st Statement object to the database
|
|
||||||
* @param metaData lastlogin column meta data
|
|
||||||
*/
|
|
||||||
private void migrateLastLoginColumn(Statement st, DatabaseMetaData metaData) throws SQLException {
|
|
||||||
final int columnType;
|
|
||||||
try (ResultSet rs = metaData.getColumns(null, null, tableName, col.LAST_LOGIN)) {
|
|
||||||
if (!rs.next()) {
|
|
||||||
ConsoleLogger.warning("Could not get LAST_LOGIN meta data. This should never happen!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
columnType = rs.getInt("DATA_TYPE");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (columnType == Types.TIMESTAMP) {
|
|
||||||
migrateLastLoginColumnFromTimestamp(st);
|
|
||||||
} else if (columnType == Types.INTEGER) {
|
|
||||||
migrateLastLoginColumnFromInt(st);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs conversion of lastlogin column from timestamp type to bigint.
|
|
||||||
*
|
|
||||||
* @param st Statement object to the database
|
|
||||||
* @see <a href="https://github.com/AuthMe/AuthMeReloaded/issues/477">#477</a>
|
|
||||||
*/
|
|
||||||
private void migrateLastLoginColumnFromTimestamp(Statement st) throws SQLException {
|
|
||||||
ConsoleLogger.info("Migrating lastlogin column from timestamp to bigint");
|
|
||||||
final String lastLoginOld = col.LAST_LOGIN + "_old";
|
|
||||||
|
|
||||||
// Rename lastlogin to lastlogin_old
|
|
||||||
String sql = String.format("ALTER TABLE %s CHANGE COLUMN %s %s BIGINT",
|
|
||||||
tableName, col.LAST_LOGIN, lastLoginOld);
|
|
||||||
st.execute(sql);
|
|
||||||
|
|
||||||
// Create lastlogin column
|
|
||||||
sql = String.format("ALTER TABLE %s ADD COLUMN %s "
|
|
||||||
+ "BIGINT NOT NULL DEFAULT 0 AFTER %s",
|
|
||||||
tableName, col.LAST_LOGIN, col.LAST_IP);
|
|
||||||
st.execute(sql);
|
|
||||||
|
|
||||||
// Set values of lastlogin based on lastlogin_old
|
|
||||||
sql = String.format("UPDATE %s SET %s = UNIX_TIMESTAMP(%s) * 1000",
|
|
||||||
tableName, col.LAST_LOGIN, lastLoginOld);
|
|
||||||
st.execute(sql);
|
|
||||||
|
|
||||||
// Drop lastlogin_old
|
|
||||||
sql = String.format("ALTER TABLE %s DROP COLUMN %s",
|
|
||||||
tableName, lastLoginOld);
|
|
||||||
st.execute(sql);
|
|
||||||
ConsoleLogger.info("Finished migration of lastlogin (timestamp to bigint)");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs conversion of lastlogin column from int to bigint.
|
|
||||||
*
|
|
||||||
* @param st Statement object to the database
|
|
||||||
* @see <a href="https://github.com/AuthMe/AuthMeReloaded/issues/887">
|
|
||||||
* #887: Migrate lastlogin column from int32 to bigint</a>
|
|
||||||
*/
|
|
||||||
private void migrateLastLoginColumnFromInt(Statement st) throws SQLException {
|
|
||||||
// Change from int to bigint
|
|
||||||
ConsoleLogger.info("Migrating lastlogin column from int to bigint");
|
|
||||||
String sql = String.format("ALTER TABLE %s MODIFY %s BIGINT;", tableName, col.LAST_LOGIN);
|
|
||||||
st.execute(sql);
|
|
||||||
|
|
||||||
// Migrate timestamps in seconds format to milliseconds format if they are plausible
|
|
||||||
int rangeStart = 1262304000; // timestamp for 2010-01-01
|
|
||||||
int rangeEnd = 1514678400; // timestamp for 2017-12-31
|
|
||||||
sql = String.format("UPDATE %s SET %s = %s * 1000 WHERE %s > %d AND %s < %d;",
|
|
||||||
tableName, col.LAST_LOGIN, col.LAST_LOGIN, col.LAST_LOGIN, rangeStart, col.LAST_LOGIN, rangeEnd);
|
|
||||||
int changedRows = st.executeUpdate(sql);
|
|
||||||
|
|
||||||
ConsoleLogger.warning("You may have entries with invalid timestamps. Please check your data "
|
|
||||||
+ "before purging. " + changedRows + " rows were migrated from seconds to milliseconds.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the column for registration date and sets all entries to the current timestamp.
|
|
||||||
* We do so in order to avoid issues with purging, where entries with 0 / NULL might get
|
|
||||||
* purged immediately on startup otherwise.
|
|
||||||
*
|
|
||||||
* @param st Statement object to the database
|
|
||||||
*/
|
|
||||||
private void addRegistrationDateColumn(Statement st) throws SQLException {
|
|
||||||
st.executeUpdate("ALTER TABLE " + tableName
|
|
||||||
+ " ADD COLUMN " + col.REGISTRATION_DATE + " BIGINT NOT NULL;");
|
|
||||||
|
|
||||||
// Use the timestamp from Java to avoid timezone issues in case JVM and database are out of sync
|
|
||||||
long currentTimestamp = System.currentTimeMillis();
|
|
||||||
int updatedRows = st.executeUpdate(String.format("UPDATE %s SET %s = %d;",
|
|
||||||
tableName, col.REGISTRATION_DATE, currentTimestamp));
|
|
||||||
ConsoleLogger.info("Created column '" + col.REGISTRATION_DATE + "' and set the current timestamp, "
|
|
||||||
+ currentTimestamp + ", to all " + updatedRows + " rows");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
151
src/main/java/fr/xephi/authme/datasource/MySqlMigrater.java
Normal file
151
src/main/java/fr/xephi/authme/datasource/MySqlMigrater.java
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
package fr.xephi.authme.datasource;
|
||||||
|
|
||||||
|
import fr.xephi.authme.ConsoleLogger;
|
||||||
|
|
||||||
|
import java.sql.DatabaseMetaData;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.sql.Types;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs migrations on the MySQL data source if necessary.
|
||||||
|
*/
|
||||||
|
final class MySqlMigrater {
|
||||||
|
|
||||||
|
private MySqlMigrater() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the last IP column to be nullable if it has a NOT NULL constraint without a default value.
|
||||||
|
* Background: Before 5.2, the last IP column was initialized to be {@code NOT NULL} without a default.
|
||||||
|
* With the introduction of a registration IP column we no longer want to set the last IP column on registration.
|
||||||
|
*
|
||||||
|
* @param st Statement object to the database
|
||||||
|
* @param metaData column metadata for the table
|
||||||
|
* @param tableName the MySQL table's name
|
||||||
|
* @param col the column names configuration
|
||||||
|
*/
|
||||||
|
static void migrateLastIpColumn(Statement st, DatabaseMetaData metaData,
|
||||||
|
String tableName, Columns col) throws SQLException {
|
||||||
|
final boolean isNotNullWithoutDefault = SqlDataSourceUtils.isNotNullColumn(metaData, tableName, col.LAST_IP)
|
||||||
|
&& SqlDataSourceUtils.getColumnDefaultValue(metaData, tableName, col.LAST_IP) == null;
|
||||||
|
|
||||||
|
if (isNotNullWithoutDefault) {
|
||||||
|
String sql = String.format("ALTER TABLE %s MODIFY %s VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin",
|
||||||
|
tableName, col.LAST_IP);
|
||||||
|
st.execute(sql);
|
||||||
|
ConsoleLogger.info("Changed last login column to allow NULL values. Please verify the registration feature "
|
||||||
|
+ "if you are hooking into a forum.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the last login column has a type that needs to be migrated.
|
||||||
|
*
|
||||||
|
* @param st Statement object to the database
|
||||||
|
* @param metaData column metadata for the table
|
||||||
|
* @param tableName the MySQL table's name
|
||||||
|
* @param col the column names configuration
|
||||||
|
*/
|
||||||
|
static void migrateLastLoginColumn(Statement st, DatabaseMetaData metaData,
|
||||||
|
String tableName, Columns col) throws SQLException {
|
||||||
|
final int columnType;
|
||||||
|
try (ResultSet rs = metaData.getColumns(null, null, tableName, col.LAST_LOGIN)) {
|
||||||
|
if (!rs.next()) {
|
||||||
|
ConsoleLogger.warning("Could not get LAST_LOGIN meta data. This should never happen!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
columnType = rs.getInt("DATA_TYPE");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (columnType == Types.TIMESTAMP) {
|
||||||
|
migrateLastLoginColumnFromTimestamp(st, tableName, col);
|
||||||
|
} else if (columnType == Types.INTEGER) {
|
||||||
|
migrateLastLoginColumnFromInt(st, tableName, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs conversion of lastlogin column from timestamp type to bigint.
|
||||||
|
*
|
||||||
|
* @param st Statement object to the database
|
||||||
|
* @param tableName the table name
|
||||||
|
* @param col the column names configuration
|
||||||
|
* @see <a href="https://github.com/AuthMe/AuthMeReloaded/issues/477">#477</a>
|
||||||
|
*/
|
||||||
|
private static void migrateLastLoginColumnFromTimestamp(Statement st, String tableName,
|
||||||
|
Columns col) throws SQLException {
|
||||||
|
ConsoleLogger.info("Migrating lastlogin column from timestamp to bigint");
|
||||||
|
final String lastLoginOld = col.LAST_LOGIN + "_old";
|
||||||
|
|
||||||
|
// Rename lastlogin to lastlogin_old
|
||||||
|
String sql = String.format("ALTER TABLE %s CHANGE COLUMN %s %s BIGINT",
|
||||||
|
tableName, col.LAST_LOGIN, lastLoginOld);
|
||||||
|
st.execute(sql);
|
||||||
|
|
||||||
|
// Create lastlogin column
|
||||||
|
sql = String.format("ALTER TABLE %s ADD COLUMN %s "
|
||||||
|
+ "BIGINT NOT NULL DEFAULT 0 AFTER %s",
|
||||||
|
tableName, col.LAST_LOGIN, col.LAST_IP);
|
||||||
|
st.execute(sql);
|
||||||
|
|
||||||
|
// Set values of lastlogin based on lastlogin_old
|
||||||
|
sql = String.format("UPDATE %s SET %s = UNIX_TIMESTAMP(%s) * 1000",
|
||||||
|
tableName, col.LAST_LOGIN, lastLoginOld);
|
||||||
|
st.execute(sql);
|
||||||
|
|
||||||
|
// Drop lastlogin_old
|
||||||
|
sql = String.format("ALTER TABLE %s DROP COLUMN %s",
|
||||||
|
tableName, lastLoginOld);
|
||||||
|
st.execute(sql);
|
||||||
|
ConsoleLogger.info("Finished migration of lastlogin (timestamp to bigint)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs conversion of lastlogin column from int to bigint.
|
||||||
|
*
|
||||||
|
* @param st Statement object to the database
|
||||||
|
* @param tableName the table name
|
||||||
|
* @param col the column names configuration
|
||||||
|
* @see <a href="https://github.com/AuthMe/AuthMeReloaded/issues/887">
|
||||||
|
* #887: Migrate lastlogin column from int32 to bigint</a>
|
||||||
|
*/
|
||||||
|
private static void migrateLastLoginColumnFromInt(Statement st, String tableName, Columns col) throws SQLException {
|
||||||
|
// Change from int to bigint
|
||||||
|
ConsoleLogger.info("Migrating lastlogin column from int to bigint");
|
||||||
|
String sql = String.format("ALTER TABLE %s MODIFY %s BIGINT;", tableName, col.LAST_LOGIN);
|
||||||
|
st.execute(sql);
|
||||||
|
|
||||||
|
// Migrate timestamps in seconds format to milliseconds format if they are plausible
|
||||||
|
int rangeStart = 1262304000; // timestamp for 2010-01-01
|
||||||
|
int rangeEnd = 1514678400; // timestamp for 2017-12-31
|
||||||
|
sql = String.format("UPDATE %s SET %s = %s * 1000 WHERE %s > %d AND %s < %d;",
|
||||||
|
tableName, col.LAST_LOGIN, col.LAST_LOGIN, col.LAST_LOGIN, rangeStart, col.LAST_LOGIN, rangeEnd);
|
||||||
|
int changedRows = st.executeUpdate(sql);
|
||||||
|
|
||||||
|
ConsoleLogger.warning("You may have entries with invalid timestamps. Please check your data "
|
||||||
|
+ "before purging. " + changedRows + " rows were migrated from seconds to milliseconds.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the column for registration date and sets all entries to the current timestamp.
|
||||||
|
* We do so in order to avoid issues with purging, where entries with 0 / NULL might get
|
||||||
|
* purged immediately on startup otherwise.
|
||||||
|
*
|
||||||
|
* @param st Statement object to the database
|
||||||
|
* @param tableName the table name
|
||||||
|
* @param col the column names configuration
|
||||||
|
*/
|
||||||
|
static void addRegistrationDateColumn(Statement st, String tableName, Columns col) throws SQLException {
|
||||||
|
st.executeUpdate("ALTER TABLE " + tableName
|
||||||
|
+ " ADD COLUMN " + col.REGISTRATION_DATE + " BIGINT NOT NULL DEFAULT 0;");
|
||||||
|
|
||||||
|
// Use the timestamp from Java to avoid timezone issues in case JVM and database are out of sync
|
||||||
|
long currentTimestamp = System.currentTimeMillis();
|
||||||
|
int updatedRows = st.executeUpdate(String.format("UPDATE %s SET %s = %d;",
|
||||||
|
tableName, col.REGISTRATION_DATE, currentTimestamp));
|
||||||
|
ConsoleLogger.info("Created column '" + col.REGISTRATION_DATE + "' and set the current timestamp, "
|
||||||
|
+ currentTimestamp + ", to all " + updatedRows + " rows");
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,10 @@
|
|||||||
package fr.xephi.authme.datasource;
|
package fr.xephi.authme.datasource;
|
||||||
|
|
||||||
import fr.xephi.authme.ConsoleLogger;
|
import fr.xephi.authme.ConsoleLogger;
|
||||||
import fr.xephi.authme.initialization.DataFolder;
|
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||||
import fr.xephi.authme.util.FileUtils;
|
import fr.xephi.authme.util.FileUtils;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@ -24,15 +22,12 @@ import java.util.Date;
|
|||||||
*/
|
*/
|
||||||
class SqLiteMigrater {
|
class SqLiteMigrater {
|
||||||
|
|
||||||
@DataFolder
|
|
||||||
private final File dataFolder;
|
private final File dataFolder;
|
||||||
|
|
||||||
private final String databaseName;
|
private final String databaseName;
|
||||||
private final String tableName;
|
private final String tableName;
|
||||||
private final Columns col;
|
private final Columns col;
|
||||||
|
|
||||||
@Inject
|
SqLiteMigrater(Settings settings, File dataFolder) {
|
||||||
SqLiteMigrater(Settings settings, @DataFolder File dataFolder) {
|
|
||||||
this.dataFolder = dataFolder;
|
this.dataFolder = dataFolder;
|
||||||
this.databaseName = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
|
this.databaseName = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
|
||||||
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||||
|
Loading…
Reference in New Issue
Block a user