mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2024-11-23 11:15:24 +01:00
Adds a single character prefix to database tables (#1278)
* Adds a single character prefix to database tables https://github.com/BentoBoxWorld/BentoBox/issues/1277 * Fix tests * Fix bug with substring
This commit is contained in:
parent
b94f9db0a9
commit
85d5a3a6ac
@ -97,6 +97,12 @@ public class Settings implements ConfigObject {
|
||||
@ConfigEntry(path = "general.database.use-ssl", since = "1.12.0")
|
||||
private boolean useSSL = false;
|
||||
|
||||
@ConfigComment("Database table prefix character. Adds a prefix to the database tables. Not used by flatfile databases.")
|
||||
@ConfigComment("Only the characters A-Z, a-z, 0-9 can be used. Invalid characters will become an underscore.")
|
||||
@ConfigComment("Set this to a unique value if you are running multiple BentoBox instances that share a database.")
|
||||
@ConfigEntry(path = "general.database.prefix-character", since = "1.13.0")
|
||||
private String databasePrefix = "";
|
||||
|
||||
@ConfigComment("Allow FTB Autonomous Activator to work (will allow a pseudo player [CoFH] to place and break blocks and hang items)")
|
||||
@ConfigComment("Add other fake player names here if required")
|
||||
@ConfigEntry(path = "general.fakeplayers", experimental = true)
|
||||
@ -610,4 +616,19 @@ public class Settings implements ConfigObject {
|
||||
public void setInviteConfirmation(boolean inviteConfirmation) {
|
||||
this.inviteConfirmation = inviteConfirmation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the databasePrefix
|
||||
*/
|
||||
public String getDatabasePrefix() {
|
||||
if (databasePrefix == null) databasePrefix = "";
|
||||
return databasePrefix.isEmpty() ? "" : databasePrefix.replaceAll("[^a-zA-Z0-9]", "_").substring(0,1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param databasePrefix the databasePrefix to set
|
||||
*/
|
||||
public void setDatabasePrefix(String databasePrefix) {
|
||||
this.databasePrefix = databasePrefix;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package world.bentobox.bentobox.commands;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
@ -47,7 +48,7 @@ public class BentoBoxMigrateCommand extends ConfirmableCommand {
|
||||
// Migrate addons data
|
||||
user.sendMessage("commands.bentobox.migrate.addons");
|
||||
getPlugin().getAddonsManager().getDataObjects().forEach(t -> {
|
||||
user.sendMessage("commands.bentobox.migrate.class", TextVariables.DESCRIPTION, t.getCanonicalName());
|
||||
user.sendMessage("commands.bentobox.migrate.class", TextVariables.DESCRIPTION, BentoBox.getInstance().getSettings().getDatabasePrefix() + t.getCanonicalName());
|
||||
new Database<>(getPlugin(), t).loadObjects();
|
||||
user.sendMessage(MIGRATED);
|
||||
});
|
||||
|
@ -58,7 +58,7 @@ public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
plugin.logError("Could not connect to the database. Are the credentials in the config.yml file correct?");
|
||||
connected = false;
|
||||
} else {
|
||||
collection = database.getCollection(dataObject.getCanonicalName());
|
||||
collection = database.getCollection(plugin.getSettings().getDatabasePrefix() + dataObject.getCanonicalName());
|
||||
IndexOptions indexOptions = new IndexOptions().unique(true);
|
||||
collection.createIndex(Indexes.text(UNIQUEID), indexOptions);
|
||||
}
|
||||
@ -140,7 +140,7 @@ public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
try {
|
||||
collection.findOneAndDelete(new Document(MONGO_ID, uniqueId));
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
plugin.logError("Could not delete object " + plugin.getSettings().getDatabasePrefix() + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import world.bentobox.bentobox.BentoBox;
|
||||
|
||||
/**
|
||||
* Contains fields that must be in any data object
|
||||
* DataObject's canonical name must be no more than 62 characters long otherwise it may not fit in a database table name
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
|
@ -84,7 +84,7 @@ public class SQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(sqlConfig.getSchemaSQL())) {
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
plugin.logError("Problem trying to create schema for data object " + plugin.getSettings().getDatabasePrefix() + dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,7 +184,7 @@ public class SQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
preparedStatement.execute();
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
plugin.logError("Could not delete object " + plugin.getSettings().getDatabasePrefix() + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,8 @@ public class MariaDBDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
* @param databaseConnector - authentication details for the database
|
||||
*/
|
||||
MariaDBDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(type.getCanonicalName())
|
||||
.schema("CREATE TABLE IF NOT EXISTS `" + type.getCanonicalName() +
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(plugin.getSettings().getDatabasePrefix() + type.getCanonicalName())
|
||||
.schema("CREATE TABLE IF NOT EXISTS `" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() +
|
||||
"` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))"));
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ public class MySQLDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
* @param dbConnecter - authentication details for the database
|
||||
*/
|
||||
MySQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector dbConnecter) {
|
||||
super(plugin, type, dbConnecter, new SQLConfiguration(type.getCanonicalName())
|
||||
.schema("CREATE TABLE IF NOT EXISTS `" + type.getCanonicalName() +
|
||||
super(plugin, type, dbConnecter, new SQLConfiguration(plugin.getSettings().getDatabasePrefix() + type.getCanonicalName())
|
||||
.schema("CREATE TABLE IF NOT EXISTS `" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() +
|
||||
"` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB"));
|
||||
}
|
||||
}
|
||||
|
@ -29,21 +29,21 @@ public class PostgreSQLDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
* @param databaseConnector Contains the settings to create a connection to the database
|
||||
*/
|
||||
PostgreSQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(type.getCanonicalName())
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(plugin.getSettings().getDatabasePrefix() + type.getCanonicalName())
|
||||
// Set uniqueid as the primary key (index). Postgresql convention is to use lower case field names
|
||||
// Postgresql also uses double quotes (") instead of (`) around tables names with dots.
|
||||
.schema("CREATE TABLE IF NOT EXISTS \"" + type.getCanonicalName() + "\" (uniqueid VARCHAR PRIMARY KEY, json jsonb NOT NULL)")
|
||||
.loadObject("SELECT * FROM \"" + type.getCanonicalName() + "\" WHERE uniqueid = ? LIMIT 1")
|
||||
.deleteObject("DELETE FROM \"" + type.getCanonicalName() + "\" WHERE uniqueid = ?")
|
||||
.schema("CREATE TABLE IF NOT EXISTS \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\" (uniqueid VARCHAR PRIMARY KEY, json jsonb NOT NULL)")
|
||||
.loadObject("SELECT * FROM \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\" WHERE uniqueid = ? LIMIT 1")
|
||||
.deleteObject("DELETE FROM \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\" WHERE uniqueid = ?")
|
||||
// uniqueId has to be added into the row explicitly so we need to override the saveObject method
|
||||
// The json value is a string but has to be cast to json when done in Java
|
||||
.saveObject("INSERT INTO \"" + type.getCanonicalName() + "\" (uniqueid, json) VALUES (?, cast(? as json)) "
|
||||
.saveObject("INSERT INTO \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\" (uniqueid, json) VALUES (?, cast(? as json)) "
|
||||
// This is the Postgresql version of UPSERT.
|
||||
+ "ON CONFLICT (uniqueid) "
|
||||
+ "DO UPDATE SET json = cast(? as json)")
|
||||
.loadObjects("SELECT json FROM \"" + type.getCanonicalName() + "\"")
|
||||
.loadObjects("SELECT json FROM \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\"")
|
||||
// Postgres exists function returns true or false natively
|
||||
.objectExists("SELECT EXISTS(SELECT * FROM \"" + type.getCanonicalName() + "\" WHERE uniqueid = ?)")
|
||||
.objectExists("SELECT EXISTS(SELECT * FROM \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\" WHERE uniqueid = ?)")
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -31,11 +31,11 @@ public class SQLiteDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
* @param databaseConnector Contains the settings to create a connection to the database
|
||||
*/
|
||||
protected SQLiteDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(type.getCanonicalName())
|
||||
.schema("CREATE TABLE IF NOT EXISTS `" + type.getCanonicalName() + "` (json JSON, uniqueId VARCHAR(255) NOT NULL PRIMARY KEY)")
|
||||
.saveObject("INSERT INTO `" + type.getCanonicalName()
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(plugin.getSettings().getDatabasePrefix() + type.getCanonicalName())
|
||||
.schema("CREATE TABLE IF NOT EXISTS `" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "` (json JSON, uniqueId VARCHAR(255) NOT NULL PRIMARY KEY)")
|
||||
.saveObject("INSERT INTO `" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName()
|
||||
+ "` (json, uniqueId) VALUES (?, ?) ON CONFLICT(uniqueId) DO UPDATE SET json = ?")
|
||||
.objectExists("SELECT EXISTS (SELECT 1 FROM `" + type.getCanonicalName() + "` WHERE `uniqueId` = ?)"));
|
||||
.objectExists("SELECT EXISTS (SELECT 1 FROM `" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "` WHERE `uniqueId` = ?)"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -71,7 +71,7 @@ public class SQLiteDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
preparedStatement.setString(1, uniqueId);
|
||||
preparedStatement.executeUpdate();
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
plugin.logError("Could not delete object " + plugin.getSettings().getDatabasePrefix() + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -61,6 +61,11 @@ general:
|
||||
# Enable SSL connection to MongoDB, MariaDB, MySQL and PostgreSQL databases.
|
||||
# Added since 1.12.0.
|
||||
use-ssl: false
|
||||
# Database table prefix character. Adds a prefix to the database tables. Not used by flatfile databases.
|
||||
# Only the characters A-Z, a-z, 0-9 can be used. Invalid characters will become an underscore.
|
||||
# Set this to a unique value if you are running multiple BentoBox instances that share a database.
|
||||
# Added since 1.13.0
|
||||
prefix-character: ''
|
||||
# Allow FTB Autonomous Activator to work (will allow a pseudo player [CoFH] to place and break blocks and hang items)
|
||||
# Add other fake player names here if required
|
||||
# /!\ This feature is experimental and might not work as expected or might not work at all.
|
||||
|
@ -34,6 +34,7 @@ import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.Settings;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
@ -78,7 +79,8 @@ public class MySQLDatabaseHandlerTest {
|
||||
private Connection connection;
|
||||
@Mock
|
||||
private PreparedStatement ps;
|
||||
|
||||
@Mock
|
||||
private Settings settings;
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
@ -88,6 +90,10 @@ public class MySQLDatabaseHandlerTest {
|
||||
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
|
||||
when(plugin.isEnabled()).thenReturn(true);
|
||||
|
||||
// Settings
|
||||
when(plugin.getSettings()).thenReturn(settings);
|
||||
when(settings.getDatabasePrefix()).thenReturn(""); // No prefix
|
||||
|
||||
// Bukkit
|
||||
PowerMockito.mockStatic(Bukkit.class);
|
||||
when(Bukkit.getScheduler()).thenReturn(sch);
|
||||
@ -146,6 +152,25 @@ public class MySQLDatabaseHandlerTest {
|
||||
assertEquals("xyz", objects.get(2).getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#loadObjects()}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testLoadObjectsPrefix() throws SQLException {
|
||||
when(settings.getDatabasePrefix()).thenReturn("a");
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(resultSet.getString(any())).thenReturn(JSON);
|
||||
// Three islands
|
||||
when(resultSet.next()).thenReturn(true, true, true, false);
|
||||
when(ps.executeQuery(Mockito.anyString())).thenReturn(resultSet);
|
||||
List<Island> objects = handler.loadObjects();
|
||||
verify(ps).executeQuery("SELECT `json` FROM `aworld.bentobox.bentobox.database.objects.Island`");
|
||||
assertTrue(objects.size() == 3);
|
||||
assertEquals("xyz", objects.get(2).getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#loadObjects()}.
|
||||
* @throws SQLException
|
||||
@ -387,6 +412,24 @@ public class MySQLDatabaseHandlerTest {
|
||||
verify(ps).setString(1, "\"hello\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testObjectExistsPrefix() throws SQLException {
|
||||
when(settings.getDatabasePrefix()).thenReturn("a");
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(ps.executeQuery()).thenReturn(resultSet);
|
||||
when(resultSet.next()).thenReturn(true);
|
||||
when(resultSet.getBoolean(eq(1))).thenReturn(true);
|
||||
assertTrue(handler.objectExists("hello"));
|
||||
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `aworld.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
|
||||
verify(ps).executeQuery();
|
||||
verify(ps).setString(1, "\"hello\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
@ -449,6 +492,17 @@ public class MySQLDatabaseHandlerTest {
|
||||
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testMySQLDatabaseHandlerCreateSchemaPrefix() throws SQLException {
|
||||
when(settings.getDatabasePrefix()).thenReturn("a");
|
||||
verify(dbConn).createConnection(any());
|
||||
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `aworld.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
|
||||
}
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
|
||||
* @throws SQLException
|
||||
|
Loading…
Reference in New Issue
Block a user