diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/mysql/MySQL.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/mysql/MySQL.java index d00e4589b..6aa095d35 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/mysql/MySQL.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/mysql/MySQL.java @@ -395,24 +395,25 @@ public ArrayList getNamesQuery() { } public String getUUID(String playerName) { - String query = "SELECT " + qi("uuid") + " FROM " + qi(tableName) + " WHERE " + qi("PlayerName") + "='" - + playerName + "';"; + String query = "SELECT " + qi("uuid") + " FROM " + qi(tableName) + " WHERE " + qi("PlayerName") + "=?;"; plugin.devDebug("DB QUERY: " + query); try (Connection conn = mysql.getConnectionManager().getConnection(); - PreparedStatement sql = conn.prepareStatement(query); - ResultSet rs = sql.executeQuery()) { - - if (rs.next()) { - if (dbType == DbType.POSTGRESQL) { - Object obj = rs.getObject(1); - if (obj instanceof java.util.UUID) { - return ((java.util.UUID) obj).toString(); + PreparedStatement sql = conn.prepareStatement(query)) { + sql.setString(1, playerName); + try (ResultSet rs = sql.executeQuery()) { + + if (rs.next()) { + if (dbType == DbType.POSTGRESQL) { + Object obj = rs.getObject(1); + if (obj instanceof java.util.UUID) { + return ((java.util.UUID) obj).toString(); + } + } + String uuid = rs.getString(1); + if (uuid != null && !uuid.isEmpty()) { + return uuid; } - } - String uuid = rs.getString(1); - if (uuid != null && !uuid.isEmpty()) { - return uuid; } } } catch (SQLException e) { @@ -537,17 +538,14 @@ public ConcurrentHashMap getRowsUUIDNameQuery() { // ------------------------- public void deletePlayer(String uuid) { - String q; - if (dbType == DbType.POSTGRESQL) { - q = "DELETE FROM " + qi(tableName) + " WHERE " + qi("uuid") + "='" + uuid + "'::uuid;"; - } else { - q = "DELETE FROM " + qi(tableName) + " WHERE " + qi("uuid") + "='" + uuid + "';"; - } + String q = "DELETE FROM " + qi(tableName) + " WHERE " + qi("uuid") + "=?;"; plugin.devDebug("DB QUERY: " + q); - try { - new Query(mysql, q).executeUpdate(); - } catch (SQLException e) { + try (Connection conn = mysql.getConnectionManager().getConnection(); + PreparedStatement statement = conn.prepareStatement(q)) { + bindUuid(statement, 1, uuid); + statement.executeUpdate(); + } catch (SQLException | IllegalArgumentException e) { debug(e); } @@ -650,53 +648,52 @@ public ArrayList getExact(String uuid) { public ArrayList getExactQuery(Column column) { ArrayList result = new ArrayList<>(); - String query; - if (dbType == DbType.POSTGRESQL && "uuid".equalsIgnoreCase(column.getName())) { - query = "SELECT * FROM " + qi(tableName) + " WHERE " + qi("uuid") + "='" + column.getValue().getString() - + "'::uuid;"; - } else { - query = "SELECT * FROM " + qi(tableName) + " WHERE " + qi(column.getName()) + "='" - + column.getValue().getString() + "';"; - } + String query = "SELECT * FROM " + qi(tableName) + " WHERE " + qi(column.getName()) + "=?;"; plugin.devDebug("DB QUERY: " + query); try (Connection conn = mysql.getConnectionManager().getConnection(); - PreparedStatement sql = conn.prepareStatement(query); - ResultSet rs = sql.executeQuery()) { - - if (rs.next()) { - for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { - String columnName = rs.getMetaData().getColumnLabel(i); - Column rCol; - - if (plugin.getUserManager().getDataManager().isInt(columnName)) { - rCol = new Column(columnName, DataType.INTEGER); - try { - rCol.setValue(new DataValueInt(rs.getInt(i))); - } catch (Exception e) { - String data = rs.getString(i); - if (data != null) { - try { - rCol.setValue(new DataValueInt(Integer.parseInt(data))); - } catch (NumberFormatException ex) { + PreparedStatement sql = conn.prepareStatement(query)) { + if (dbType == DbType.POSTGRESQL && "uuid".equalsIgnoreCase(column.getName())) { + bindUuid(sql, 1, column.getValue().getString()); + } else { + bindValue(sql, 1, column.getValue()); + } + try (ResultSet rs = sql.executeQuery()) { + + if (rs.next()) { + for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { + String columnName = rs.getMetaData().getColumnLabel(i); + Column rCol; + + if (plugin.getUserManager().getDataManager().isInt(columnName)) { + rCol = new Column(columnName, DataType.INTEGER); + try { + rCol.setValue(new DataValueInt(rs.getInt(i))); + } catch (Exception e) { + String data = rs.getString(i); + if (data != null) { + try { + rCol.setValue(new DataValueInt(Integer.parseInt(data))); + } catch (NumberFormatException ex) { + rCol.setValue(new DataValueInt(0)); + } + } else { rCol.setValue(new DataValueInt(0)); } - } else { - rCol.setValue(new DataValueInt(0)); } + } else if (plugin.getUserManager().getDataManager().isBoolean(columnName)) { + rCol = new Column(columnName, DataType.BOOLEAN); + rCol.setValue(new DataValueBoolean(Boolean.valueOf(rs.getString(i)))); + } else { + rCol = new Column(columnName, DataType.STRING); + rCol.setValue(new DataValueString(rs.getString(i))); } - } else if (plugin.getUserManager().getDataManager().isBoolean(columnName)) { - rCol = new Column(columnName, DataType.BOOLEAN); - rCol.setValue(new DataValueBoolean(Boolean.valueOf(rs.getString(i)))); - } else { - rCol = new Column(columnName, DataType.STRING); - rCol.setValue(new DataValueString(rs.getString(i))); + result.add(rCol); } - result.add(rCol); } + return result; } - return result; } catch (SQLException | ArrayIndexOutOfBoundsException e) { debug(e); } @@ -728,10 +725,10 @@ public void insertQuery(String index, List cols) { sb.append(", ").append(qi(col.getName())); } - sb.append(") VALUES ('").append(index).append("'::uuid"); + sb.append(") VALUES (?"); for (Column col : cols) { - sb.append(", '").append(col.getValue().toString()).append("'"); + sb.append(", ?"); } sb.append(") ON CONFLICT (").append(qi("uuid")).append(") DO UPDATE SET "); @@ -749,32 +746,37 @@ public void insertQuery(String index, List cols) { plugin.devDebug("DB QUERY: " + query); try { - new Query(mysql, query).executeUpdate(); + Query prepared = new Query(mysql, query); + prepared.setParameter(1, UUID.fromString(index)); + for (int i = 0; i < cols.size(); i++) { + prepared.setParameter(i + 2, toSqlValue(cols.get(i).getValue())); + } + prepared.executeUpdate(); } catch (Exception e) { debug(e); plugin.debug("Failed to insert/upsert player " + index); } } else { - // MySQL/MariaDB: keep original INSERT IGNORE ... SET ... - String query = "INSERT IGNORE " + qi(tableName) + " set " + qi("uuid") + "='" + index + "', "; - + StringBuilder query = new StringBuilder("INSERT IGNORE INTO ").append(qi(tableName)).append(" (") + .append(qi("uuid")); + for (Column col : cols) { + query.append(", ").append(qi(col.getName())); + } + query.append(") VALUES (?"); for (int i = 0; i < cols.size(); i++) { - Column col = cols.get(i); - boolean last = (i == cols.size() - 1); - - if (col.getValue().isString()) { - query += qi(col.getName()) + "='" + col.getValue().getString() + "'" + (last ? ";" : ", "); - } else if (col.getValue().isBoolean()) { - query += qi(col.getName()) + "='" + col.getValue().getBoolean() + "'" + (last ? ";" : ", "); - } else if (col.getValue().isInt()) { - query += qi(col.getName()) + "='" + col.getValue().getInt() + "'" + (last ? ";" : ", "); - } + query.append(", ?"); } + query.append(");"); - plugin.devDebug("DB QUERY: " + query); + plugin.devDebug("DB QUERY: " + query.toString()); try { - new Query(mysql, query).executeUpdate(); + Query prepared = new Query(mysql, query.toString()); + prepared.setParameter(1, index); + for (int i = 0; i < cols.size(); i++) { + prepared.setParameter(i + 2, toSqlValue(cols.get(i).getValue())); + } + prepared.executeUpdate(); } catch (Exception e) { debug(e); plugin.debug("Failed to insert player " + index); @@ -803,6 +805,9 @@ public void update(String index, List cols, boolean runAsync) { for (Column col : cols) { checkColumn(col.getName(), col.getDataType()); } + if (cols.isEmpty()) { + return; + } synchronized (updateLock) { if (getUuids().contains(index) || containsKeyQuery(index)) { @@ -810,33 +815,23 @@ public void update(String index, List cols, boolean runAsync) { sb.append("UPDATE ").append(qi(tableName)).append(" SET "); for (int i = 0; i < cols.size(); i++) { - Column col = cols.get(i); - boolean last = (i == cols.size() - 1); - - if (col.getValue().isString()) { - sb.append(qi(col.getName())).append("='").append(col.getValue().getString()).append("'"); - } else if (col.getValue().isBoolean()) { - sb.append(qi(col.getName())).append("='").append(col.getValue().getBoolean()).append("'"); - } else if (col.getValue().isInt()) { - sb.append(qi(col.getName())).append("='").append(col.getValue().getInt()).append("'"); - } - - if (!last) { + sb.append(qi(cols.get(i).getName())).append("=?"); + if (i != cols.size() - 1) { sb.append(", "); } } - if (dbType == DbType.POSTGRESQL) { - sb.append(" WHERE ").append(qi("uuid")).append("='").append(index).append("'::uuid;"); - } else { - sb.append(" WHERE ").append(qi("uuid")).append("='").append(index).append("';"); - } + sb.append(" WHERE ").append(qi("uuid")).append("=?;"); String query = sb.toString(); plugin.devDebug("DB QUERY: " + query); try { Query q = new Query(mysql, query); + for (int i = 0; i < cols.size(); i++) { + q.setParameter(i + 1, toSqlValue(cols.get(i).getValue())); + } + q.setParameter(cols.size() + 1, toUuidValue(index)); if (runAsync) { q.executeUpdateAsync(); } else { @@ -861,26 +856,15 @@ public void update(String index, String column, DataValue value) { synchronized (updateLock) { if (getUuids().contains(index) || containsKeyQuery(index)) { - String query = "UPDATE " + qi(tableName) + " SET "; - - if (value.isString()) { - query += qi(column) + "='" + value.getString() + "'"; - } else if (value.isBoolean()) { - query += qi(column) + "='" + value.getBoolean() + "'"; - } else if (value.isInt()) { - query += qi(column) + "='" + value.getInt() + "'"; - } - - if (dbType == DbType.POSTGRESQL) { - query += " WHERE " + qi("uuid") + "='" + index + "'::uuid;"; - } else { - query += " WHERE " + qi("uuid") + "='" + index + "';"; - } + String query = "UPDATE " + qi(tableName) + " SET " + qi(column) + "=? WHERE " + qi("uuid") + "=?;"; plugin.devDebug("DB QUERY: " + query); try { - new Query(mysql, query).executeUpdate(); + Query prepared = new Query(mysql, query); + prepared.setParameter(1, toSqlValue(value)); + prepared.setParameter(2, toUuidValue(index)); + prepared.executeUpdate(); } catch (SQLException e) { debug(e); } @@ -890,6 +874,34 @@ public void update(String index, String column, DataValue value) { } } + private void bindUuid(PreparedStatement statement, int parameter, String uuid) throws SQLException { + statement.setObject(parameter, toUuidValue(uuid)); + } + + private void bindValue(PreparedStatement statement, int parameter, DataValue value) throws SQLException { + statement.setObject(parameter, toSqlValue(value)); + } + + private Object toUuidValue(String uuid) { + return dbType == DbType.POSTGRESQL ? UUID.fromString(uuid) : uuid; + } + + private Object toSqlValue(DataValue value) { + if (value == null) { + return null; + } + if (value.isString()) { + return value.getString(); + } + if (value.isBoolean()) { + return String.valueOf(value.getBoolean()); + } + if (value.isInt()) { + return value.getInt(); + } + return value.toString(); + } + public void wipeColumnData(String columnName, DataType dataType) { checkColumn(columnName, dataType); diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/sql/UserTable.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/sql/UserTable.java index b005cadc0..b28bc63f3 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/sql/UserTable.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/sql/UserTable.java @@ -597,9 +597,10 @@ public ArrayList getTableColumns() { } public String getUUID(String playerName) { - String query = "SELECT uuid FROM " + getName() + " WHERE " + "PlayerName" + "='" + playerName + "';"; + String query = "SELECT uuid FROM " + getName() + " WHERE PlayerName=?;"; try (PreparedStatement sql = sqLite.getSQLConnection().prepareStatement(query)) { + sql.setString(1, playerName); ResultSet rs = sql.executeQuery(); /* * Query sql = new Query(mysql, query); ResultSet rs = sql.executeQuery(); @@ -729,29 +730,26 @@ public void update(Column primaryKey, List columns) { for (Column c : columns) { checkColumn(c); } + if (columns.isEmpty()) { + return; + } if (containsKey(primaryKey.getValue().toString())) { synchronized (object) { - String query = "UPDATE " + getName() + " SET "; - for (Column column : columns) { - if (column.getValue().isString()) { - query += "`" + column.getName() + "`='" + column.getValue().getString() + "'"; - } else if (column.getValue().isBoolean()) { - query += "`" + column.getName() + "`=" + column.getValue().getBoolean(); - } else if (column.getValue().isInt()) { - query += "`" + column.getName() + "`=" + column.getValue().getInt(); - } - if (columns.indexOf(column) == columns.size() - 1) { - query += " "; - } else { - query += ", "; + StringBuilder query = new StringBuilder("UPDATE ").append(getName()).append(" SET "); + for (int i = 0; i < columns.size(); i++) { + query.append("`").append(columns.get(i).getName()).append("`=?"); + if (i != columns.size() - 1) { + query.append(", "); } } - query += "WHERE `" + primaryKey.getName() + "`="; - query += "'" + primaryKey.getValue().getString() + "'"; - try { - PreparedStatement s = sqLite.getSQLConnection().prepareStatement(query); + query.append(" WHERE `").append(primaryKey.getName()).append("`=?"); + try (PreparedStatement s = sqLite.getSQLConnection().prepareStatement(query.toString())) { + int parameter = 1; + for (Column column : columns) { + bindValue(s, parameter++, column); + } + bindValue(s, parameter, primaryKey); s.executeUpdate(); - s.close(); } catch (SQLException e) { e.printStackTrace(); } @@ -770,6 +768,20 @@ public void update(Column primaryKey, List columns) { } } + private void bindValue(PreparedStatement statement, int parameter, Column column) throws SQLException { + if (column.getValue() == null) { + statement.setObject(parameter, null); + } else if (column.getValue().isString()) { + statement.setString(parameter, column.getValue().getString()); + } else if (column.getValue().isBoolean()) { + statement.setBoolean(parameter, column.getValue().getBoolean()); + } else if (column.getValue().isInt()) { + statement.setInt(parameter, column.getValue().getInt()); + } else { + statement.setObject(parameter, column.getValue().toString()); + } + } + public void wipeColumnData(String columnName, DataType dataType) { checkColumn(new Column(columnName, dataType)); String sql = "UPDATE " + getName() + " SET " + columnName + " = " + dataType.getNoValue() + ";"; @@ -786,4 +798,4 @@ public SQLite getSqLite() { return sqLite; } -} \ No newline at end of file +} diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/bungeeapi/globaldata/GlobalMySQL.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/bungeeapi/globaldata/GlobalMySQL.java index df2124803..99ed8d825 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/bungeeapi/globaldata/GlobalMySQL.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/bungeeapi/globaldata/GlobalMySQL.java @@ -323,15 +323,14 @@ public void alterColumnType(final String column, final String newType) { DbType dbType = dbType(); String normalized = normalizeColumnType(dbType, newType); + boolean integerType = normalized.toUpperCase().contains("INT"); // First inspect existing type; skip ALTER if it's already correct try (Connection conn = mysql.getConnectionManager().getConnection()) { if (!columnNeedsAlter(conn, dbType, column, normalized)) { debugLog("GlobalDB: Column " + qi(dbType == DbType.POSTGRESQL ? column.toLowerCase() : column) + " already matches " + normalized + ", skipping ALTER"); - if (normalized.toUpperCase().contains("INT") && !intColumns.contains(column)) { - intColumns.add(column); - } + trackIntegerColumn(column, integerType); return; } } catch (SQLException e) { @@ -341,38 +340,54 @@ public void alterColumnType(final String column, final String newType) { debugLog("Altering column `" + column + "` to " + normalized); - // If going to INT, normalise empty strings to 0 first to avoid conversion issues. - if (normalized.toUpperCase().contains("INT")) { + // MySQL needs empty text values normalized before changing to an integer type. + if (integerType && dbType != DbType.POSTGRESQL) { try { - if (dbType == DbType.POSTGRESQL) { - // Postgres: trim(coalesce(col::text,'')) = '' - String fix = "UPDATE " + getName() + " SET " + qi(column.toLowerCase()) + " = '0' " - + "WHERE btrim(coalesce(" + qi(column.toLowerCase()) + "::text, '')) = '';"; - new Query(mysql, fix).executeUpdateAsync(); - } else { - String fix = "UPDATE " + getName() + " SET " + qi(column) + " = '0' " - + "WHERE TRIM(COALESCE(" + column + ", '')) = '';"; - new Query(mysql, fix).executeUpdateAsync(); - } + String fix = "UPDATE " + getName() + " SET " + qi(column) + " = '0' " + + "WHERE TRIM(COALESCE(" + qi(column) + ", '')) = '';"; + new Query(mysql, fix).executeUpdate(); } catch (SQLException e) { - e.printStackTrace(); + debugEx(e); + return; } } try { String alter; if (dbType == DbType.POSTGRESQL) { - alter = "ALTER TABLE " + getName() + " ALTER COLUMN " + qi(column.toLowerCase()) + " TYPE " + normalized - + ";"; + String columnName = qi(column.toLowerCase()); + alter = "ALTER TABLE " + getName() + " ALTER COLUMN " + columnName + " TYPE " + normalized; + if (integerType) { + alter += " USING CASE WHEN btrim(coalesce(" + columnName + "::text, '')) = '' THEN 0 ELSE " + + columnName + "::" + normalized + " END"; + } + alter += ";"; } else { alter = "ALTER TABLE " + getName() + " MODIFY " + qi(column) + " " + normalized + ";"; } - new Query(mysql, alter).executeUpdateAsync(); + new Query(mysql, alter).executeUpdate(); } catch (SQLException e) { - e.printStackTrace(); + debugEx(e); + return; } - if (normalized.toUpperCase().contains("INT") && !intColumns.contains(column)) { + try (Connection conn = mysql.getConnectionManager().getConnection()) { + if (columnNeedsAlter(conn, dbType, column, normalized)) { + debugLog("GlobalDB: Column " + column + " did not change to " + normalized); + return; + } + } catch (SQLException e) { + debugLog("GlobalDB: Unable to verify column type for " + getName() + "." + column); + debugEx(e); + return; + } + + trackIntegerColumn(column, integerType); + } + + private void trackIntegerColumn(String column, boolean integerType) { + intColumns.removeIf(intColumn -> intColumn.equalsIgnoreCase(column)); + if (integerType) { intColumns.add(column); } } @@ -480,9 +495,11 @@ public boolean containsServer(String server) { * @param server the server name to delete */ public void deleteServer(String server) { - String q = "DELETE FROM " + getName() + " WHERE server='" + server + "';"; + String q = "DELETE FROM " + getName() + " WHERE " + qi("server") + "=?;"; try { - new Query(mysql, q).executeUpdate(); + Query prepared = new Query(mysql, q); + prepared.setParameter(1, server); + prepared.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } @@ -576,27 +593,29 @@ public ArrayList getExactQuery(Column column) { ArrayList result = new ArrayList<>(); String colName = (dbType() == DbType.POSTGRESQL) ? qi(column.getName().toLowerCase()) : qi(column.getName()); - String query = "SELECT * FROM " + getName() + " WHERE " + colName + "='" + column.getValue().getString() + "';"; + String query = "SELECT * FROM " + getName() + " WHERE " + colName + "=?;"; try (Connection conn = mysql.getConnectionManager().getConnection(); - PreparedStatement sql = conn.prepareStatement(query); - ResultSet rs = sql.executeQuery()) { - - if (rs.next()) { - for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { - String columnName = rs.getMetaData().getColumnLabel(i); - Column rCol; - if (intColumns.contains(columnName)) { - rCol = new Column(columnName, DataType.INTEGER); - rCol.setValue(new DataValueInt(rs.getInt(i))); - } else { - rCol = new Column(columnName, DataType.STRING); - rCol.setValue(new DataValueString(rs.getString(i))); + PreparedStatement sql = conn.prepareStatement(query)) { + sql.setObject(1, toSqlValue(column.getName(), column.getValue())); + try (ResultSet rs = sql.executeQuery()) { + + if (rs.next()) { + for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { + String columnName = rs.getMetaData().getColumnLabel(i); + Column rCol; + if (isIntColumn(columnName)) { + rCol = new Column(columnName, DataType.INTEGER); + rCol.setValue(new DataValueInt(rs.getInt(i))); + } else { + rCol = new Column(columnName, DataType.STRING); + rCol.setValue(new DataValueString(rs.getString(i))); + } + result.add(rCol); } - result.add(rCol); } + return result; } - return result; } catch (SQLException | ArrayIndexOutOfBoundsException e) { e.printStackTrace(); } @@ -714,9 +733,9 @@ public void insertQuery(String index, List cols) { for (Column col : cols) { sb.append(", ").append(quoteIdent(dbType, col.getName().toLowerCase())); } - sb.append(") VALUES ('").append(index).append("'"); + sb.append(") VALUES (?"); for (Column col : cols) { - sb.append(", '").append(col.getValue().toString()).append("'"); + sb.append(", ?"); } sb.append(") ON CONFLICT (server) DO UPDATE SET "); for (int i = 0; i < cols.size(); i++) { @@ -731,7 +750,13 @@ public void insertQuery(String index, List cols) { String query = sb.toString(); try { - new Query(mysql, query).executeUpdate(); + Query prepared = new Query(mysql, query); + prepared.setParameter(1, index); + for (int i = 0; i < cols.size(); i++) { + Column column = cols.get(i); + prepared.setParameter(i + 2, toSqlValue(column.getName(), column.getValue())); + } + prepared.executeUpdate(); servers.add(index); debugLog("Upserting " + index + " into database"); } catch (Exception e) { @@ -741,25 +766,25 @@ public void insertQuery(String index, List cols) { return; } - // MySQL/MariaDB: keep original INSERT IGNORE ... SET ... - String query = "INSERT IGNORE " + getName() + " "; - query += "set server='" + index + "', "; - + StringBuilder query = new StringBuilder("INSERT IGNORE INTO ").append(getName()).append(" (") + .append(qi("server")); + for (Column col : cols) { + query.append(", ").append(qi(col.getName())); + } + query.append(") VALUES (?"); for (int i = 0; i < cols.size(); i++) { - Column col = cols.get(i); - boolean last = (i == cols.size() - 1); - - if (col.getValue().isString()) { - query += col.getName() + "='" + col.getValue().getString() + "'" + (last ? ";" : ", "); - } else if (col.getValue().isBoolean()) { - query += col.getName() + "='" + col.getValue().getBoolean() + "'" + (last ? ";" : ", "); - } else if (col.getValue().isInt()) { - query += col.getName() + "='" + col.getValue().getInt() + "'" + (last ? ";" : ", "); - } + query.append(", ?"); } + query.append(");"); try { - new Query(mysql, query).executeUpdate(); + Query prepared = new Query(mysql, query.toString()); + prepared.setParameter(1, index); + for (int i = 0; i < cols.size(); i++) { + Column column = cols.get(i); + prepared.setParameter(i + 2, toSqlValue(column.getName(), column.getValue())); + } + prepared.executeUpdate(); servers.add(index); debugLog("Inserting " + index + " into database"); } catch (Exception e) { @@ -775,7 +800,15 @@ public void insertQuery(String index, List cols) { * @return true if the column is an integer column, false otherwise */ public boolean isIntColumn(String key) { - return intColumns.contains(key); + if (key == null) { + return false; + } + for (String intColumn : intColumns) { + if (intColumn.equalsIgnoreCase(key)) { + return true; + } + } + return false; } /** @@ -818,6 +851,9 @@ public void update(String index, List cols, boolean runAsync) { for (Column col : cols) { checkColumn(col.getName(), col.getDataType()); } + if (cols.isEmpty()) { + return; + } synchronized (object2) { if (getServers().contains(index) || containsKeyQuery(index)) { @@ -829,31 +865,26 @@ public void update(String index, List cols, boolean runAsync) { for (int i = 0; i < cols.size(); i++) { Column col = cols.get(i); - boolean last = (i == cols.size() - 1); - String colName = (dbType == DbType.POSTGRESQL) ? quoteIdent(dbType, col.getName().toLowerCase()) - : "`" + col.getName() + "`"; - - if (col.getValue().isString()) { - sb.append(colName).append("='").append(col.getValue().getString()).append("'"); - } else if (col.getValue().isBoolean()) { - sb.append(colName).append("='").append(col.getValue().getBoolean()).append("'"); - } else if (col.getValue().isInt()) { - sb.append(colName).append("='").append(col.getValue().getInt()).append("'"); - } - - if (!last) { + : qi(col.getName()); + sb.append(colName).append("=?"); + if (i != cols.size() - 1) { sb.append(", "); } } - sb.append(" WHERE server='").append(index).append("';"); + sb.append(" WHERE ").append(qi("server")).append("=?;"); String query = sb.toString(); debugLog("Batch query: " + query); try { Query q = new Query(mysql, query); + for (int i = 0; i < cols.size(); i++) { + Column column = cols.get(i); + q.setParameter(i + 1, toSqlValue(column.getName(), column.getValue())); + } + q.setParameter(cols.size() + 1, index); if (runAsync) { q.executeUpdateAsync(); } else { @@ -886,22 +917,15 @@ public void update(String index, String column, DataValue value) { if (getServers().contains(index) || containsKeyQuery(index)) { DbType dbType = dbType(); - String colName = (dbType == DbType.POSTGRESQL) ? quoteIdent(dbType, column.toLowerCase()) : column; + String colName = (dbType == DbType.POSTGRESQL) ? quoteIdent(dbType, column.toLowerCase()) : qi(column); - String query = "UPDATE " + getName() + " SET "; - - if (value.isString()) { - query += colName + "='" + value.getString() + "'"; - } else if (value.isBoolean()) { - query += colName + "='" + value.getBoolean() + "'"; - } else if (value.isInt()) { - query += colName + "='" + value.getInt() + "'"; - } - - query += " WHERE server='" + index + "';"; + String query = "UPDATE " + getName() + " SET " + colName + "=? WHERE " + qi("server") + "=?;"; try { - new Query(mysql, query).executeUpdate(); + Query prepared = new Query(mysql, query); + prepared.setParameter(1, toSqlValue(column, value)); + prepared.setParameter(2, index); + prepared.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } @@ -911,6 +935,22 @@ public void update(String index, String column, DataValue value) { } } + private Object toSqlValue(String column, DataValue value) { + if (value == null) { + return null; + } + if (value.isString()) { + return value.getString(); + } + if (value.isBoolean()) { + return String.valueOf(value.getBoolean()); + } + if (value.isInt()) { + return isIntColumn(column) ? value.getInt() : String.valueOf(value.getInt()); + } + return value.toString(); + } + /** * Logs a warning message. * diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/user/UserTablePreparedStatementTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/user/UserTablePreparedStatementTest.java new file mode 100644 index 000000000..7ed758859 --- /dev/null +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/user/UserTablePreparedStatementTest.java @@ -0,0 +1,85 @@ +package com.bencodez.advancedcore.tests.user; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.user.userstorage.sql.UserTable; +import com.bencodez.simpleapi.sql.Column; +import com.bencodez.simpleapi.sql.data.DataValueInt; +import com.bencodez.simpleapi.sql.data.DataValueString; +import com.bencodez.simpleapi.sql.sqlite.db.SQLite; + +class UserTablePreparedStatementTest { + + private static final String UUID = "00000000-0000-0000-0000-000000000001"; + + private Connection connection; + private PreparedStatement statement; + private SQLite sqlite; + + @BeforeEach + void setUp() throws Exception { + connection = mock(Connection.class); + statement = mock(PreparedStatement.class); + sqlite = mock(SQLite.class); + when(sqlite.getSQLConnection()).thenReturn(connection); + } + + @Test + void updateBindsValuesInsteadOfEmbeddingThemInSql() throws Exception { + Column primaryKey = new Column("uuid", new DataValueString(UUID)); + UserTable table = spy(new UserTable(mock(AdvancedCorePlugin.class), "Users", + Collections.singletonList(primaryKey), primaryKey)); + table.setSqLite(sqlite); + doNothing().when(table).checkColumn(any(Column.class)); + doReturn(true).when(table).containsKey(anyString()); + + String sql = "UPDATE Users SET `LastVotes`=?, `Points`=? WHERE `uuid`=?"; + when(connection.prepareStatement(sql)).thenReturn(statement); + + String lastVotes = "Site's value with punctuation;//123"; + table.update(primaryKey, Arrays.asList(new Column("LastVotes", new DataValueString(lastVotes)), + new Column("Points", new DataValueInt(25)))); + + verify(connection).prepareStatement(sql); + verify(statement).setString(1, lastVotes); + verify(statement).setInt(2, 25); + verify(statement).setString(3, UUID); + verify(statement).executeUpdate(); + } + + @Test + void getUuidBindsPlayerName() throws Exception { + Column primaryKey = new Column("uuid", new DataValueString(UUID)); + UserTable table = new UserTable(mock(AdvancedCorePlugin.class), "Users", + Collections.singletonList(primaryKey), primaryKey); + table.setSqLite(sqlite); + + ResultSet resultSet = mock(ResultSet.class); + String sql = "SELECT uuid FROM Users WHERE PlayerName=?;"; + when(connection.prepareStatement(sql)).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + table.getUUID("O'Brien"); + + verify(statement).setString(1, "O'Brien"); + verify(statement).executeQuery(); + } +}