From c117bcd8543a9a6e4e1cf2984b820fff9a4e44c2 Mon Sep 17 00:00:00 2001 From: Philip Cass Date: Tue, 5 Nov 2013 21:09:14 +0000 Subject: [PATCH 1/2] Minor database change - Text fields should be UTF-8. Fixes #491 --- src/main/java/de/diddiz/LogBlock/Updater.java | 39 ++++++++++++++++++- src/main/resources/plugin.yml | 2 +- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/diddiz/LogBlock/Updater.java b/src/main/java/de/diddiz/LogBlock/Updater.java index 3888aa2..8de1123 100644 --- a/src/main/java/de/diddiz/LogBlock/Updater.java +++ b/src/main/java/de/diddiz/LogBlock/Updater.java @@ -199,6 +199,41 @@ class Updater } config.set("version", "1.52"); } + // Ensure charset for free-text fields is UTF-8 + // As this may be an expensive operation and the database default may already be UTF-8, check on a table-by-table basis before converting + if (config.getString("version").compareTo("1.71") < 0) { + getLogger().info("Updating tables to 1.71 ..."); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + if (isLogging(Logging.CHAT)) { + final ResultSet rs = st.executeQuery("SHOW FULL COLUMNS FROM `lb-chat` WHERE field = 'message'"); + if (rs.next() && !rs.getString("Collation").substring(0,4).equalsIgnoreCase("utf8")) { + st.execute("ALTER TABLE `lb-chat` CONVERT TO CHARSET utf8"); + getLogger().info("Table lb-chat modified"); + } else { + getLogger().info("Table lb-chat already fine, skipping it"); + } + } + for (final WorldConfig wcfg : getLoggedWorlds()) { + final ResultSet rs = st.executeQuery("SHOW FULL COLUMNS FROM `"+wcfg.table+"-sign` WHERE field = 'signtext'"); + if (rs.next() && !rs.getString("Collation").substring(0,4).equalsIgnoreCase("utf8")) { + st.execute("ALTER TABLE `"+wcfg.table+"-sign` CONVERT TO CHARSET utf8"); + getLogger().info("Table "+wcfg.table+"-sign modified"); + } else { + getLogger().info("Table "+wcfg.table+"-sign already fine, skipping it"); + } + } + st.close(); + conn.close(); + } catch (final SQLException ex) { + Bukkit.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.71"); + } + logblock.saveConfig(); return true; } @@ -212,10 +247,10 @@ class Updater conn.setAutoCommit(true); createTable(dbm, state, "lb-players", "(playerid INT UNSIGNED NOT NULL AUTO_INCREMENT, playername varchar(32) NOT NULL, firstlogin DATETIME NOT NULL, lastlogin DATETIME NOT NULL, onlinetime INT UNSIGNED NOT NULL, ip varchar(255) NOT NULL, PRIMARY KEY (playerid), UNIQUE (playername))"); if (isLogging(Logging.CHAT)) - createTable(dbm, state, "lb-chat", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, message VARCHAR(255) NOT NULL, PRIMARY KEY (id), KEY playerid (playerid), FULLTEXT message (message)) ENGINE=MyISAM"); + createTable(dbm, state, "lb-chat", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, message VARCHAR(255) NOT NULL, PRIMARY KEY (id), KEY playerid (playerid), FULLTEXT message (message)) ENGINE=MyISAM DEFAULT CHARSET utf8"); for (final WorldConfig wcfg : getLoggedWorlds()) { createTable(dbm, state, wcfg.table, "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, replaced TINYINT UNSIGNED NOT NULL, type TINYINT UNSIGNED NOT NULL, data TINYINT UNSIGNED NOT NULL, x MEDIUMINT NOT NULL, y SMALLINT UNSIGNED NOT NULL, z MEDIUMINT NOT NULL, PRIMARY KEY (id), KEY coords (x, z, y), KEY date (date), KEY playerid (playerid))"); - createTable(dbm, state, wcfg.table + "-sign", "(id INT UNSIGNED NOT NULL, signtext VARCHAR(255) NOT NULL, PRIMARY KEY (id))"); + createTable(dbm, state, wcfg.table + "-sign", "(id INT UNSIGNED NOT NULL, signtext VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET utf8"); createTable(dbm, state, wcfg.table + "-chest", "(id INT UNSIGNED NOT NULL, itemtype SMALLINT UNSIGNED NOT NULL, itemamount SMALLINT NOT NULL, itemdata TINYINT UNSIGNED NOT NULL, PRIMARY KEY (id))"); if (wcfg.isLogging(Logging.KILL)) createTable(dbm, state, wcfg.table + "-kills", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, killer INT UNSIGNED, victim INT UNSIGNED NOT NULL, weapon SMALLINT UNSIGNED NOT NULL, x SMALLINT NOT NULL, y TINYINT UNSIGNED NOT NULL, z SMALLINT NOT NULL, PRIMARY KEY (id))"); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 637fe4e..ce1095d 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ${project.name} -version: '1.70' +version: '1.71' author: DiddiZ authors: [md_5, ammar2] website: http://dev.bukkit.org/server-mods/logblock/ From 7ac35e46985707516f8b73778156baf6e733483b Mon Sep 17 00:00:00 2001 From: Philip Cass Date: Wed, 6 Nov 2013 10:45:13 +0000 Subject: [PATCH 2/2] Alter INSERT IGNORE queries to workaround innodb auto increment behaviour An INSERT IGNORE query will still increment when using innodb, even if no row is inserted. We can work around this by using a slightly convoluted query, but this only works if the table is non-empty, so also check for that on startup and create a dummy record if necessary. --- src/main/java/de/diddiz/LogBlock/Consumer.java | 6 ++++-- src/main/java/de/diddiz/LogBlock/Updater.java | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/diddiz/LogBlock/Consumer.java b/src/main/java/de/diddiz/LogBlock/Consumer.java index 48d8ff2..e1fb205 100644 --- a/src/main/java/de/diddiz/LogBlock/Consumer.java +++ b/src/main/java/de/diddiz/LogBlock/Consumer.java @@ -336,7 +336,8 @@ public class Consumer extends TimerTask continue; for (final String player : r.getPlayers()) if (!playerIds.containsKey(player) && !insertedPlayers.contains(player)) { - writer.println("INSERT IGNORE INTO `lb-players` (playername) VALUES ('" + player + "');"); + // Odd query contruction is to work around innodb auto increment behaviour - bug #492 + writer.println("INSERT IGNORE INTO `lb-players` (playername) SELECT '" + player + "' FROM `lb-players` WHERE NOT EXISTS (SELECT NULL FROM `lb-players` WHERE playername = '" + player + "') LIMIT 1;"); insertedPlayers.add(player); } for (final String insert : r.getInserts()) @@ -365,7 +366,8 @@ public class Consumer extends TimerTask } private boolean addPlayer(Statement state, String playerName) throws SQLException { - state.execute("INSERT IGNORE INTO `lb-players` (playername) VALUES ('" + playerName + "')"); + // Odd query contruction is to work around innodb auto increment behaviour - bug #492 + state.execute("INSERT IGNORE INTO `lb-players` (playername) SELECT '" + playerName + "' FROM `lb-players` WHERE NOT EXISTS (SELECT NULL FROM `lb-players` WHERE playername = '" + playerName + "') LIMIT 1;"); final ResultSet rs = state.executeQuery("SELECT playerid FROM `lb-players` WHERE playername = '" + playerName + "'"); if (rs.next()) playerIds.put(playerName, rs.getInt(1)); diff --git a/src/main/java/de/diddiz/LogBlock/Updater.java b/src/main/java/de/diddiz/LogBlock/Updater.java index 8de1123..6d35c79 100644 --- a/src/main/java/de/diddiz/LogBlock/Updater.java +++ b/src/main/java/de/diddiz/LogBlock/Updater.java @@ -246,6 +246,10 @@ class Updater final DatabaseMetaData dbm = conn.getMetaData(); conn.setAutoCommit(true); createTable(dbm, state, "lb-players", "(playerid INT UNSIGNED NOT NULL AUTO_INCREMENT, playername varchar(32) NOT NULL, firstlogin DATETIME NOT NULL, lastlogin DATETIME NOT NULL, onlinetime INT UNSIGNED NOT NULL, ip varchar(255) NOT NULL, PRIMARY KEY (playerid), UNIQUE (playername))"); + // Players table must not be empty or inserts won't work - bug #492 + final ResultSet rs = state.executeQuery("SELECT NULL FROM `lb-players` LIMIT 1;"); + if (!rs.next()) + state.execute("INSERT IGNORE INTO `lb-players` (playername) VALUES ('dummy_record')"); if (isLogging(Logging.CHAT)) createTable(dbm, state, "lb-chat", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, message VARCHAR(255) NOT NULL, PRIMARY KEY (id), KEY playerid (playerid), FULLTEXT message (message)) ENGINE=MyISAM DEFAULT CHARSET utf8"); for (final WorldConfig wcfg : getLoggedWorlds()) {