Skip to content

Commit ed64a73

Browse files
committed
Fix for Bug#95139 (29807572), CACHESERVERCONFIGURATION APPEARS TO THWART
CHARSET DETECTION.
1 parent 2905415 commit ed64a73

File tree

4 files changed

+161
-7
lines changed

4 files changed

+161
-7
lines changed

CHANGES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
Version 8.0.27
55

6+
- Fix for Bug#95139 (29807572), CACHESERVERCONFIGURATION APPEARS TO THWART CHARSET DETECTION.
7+
68
- Fix for Bug#104641 (33237255), DatabaseMetaData.getImportedKeys can return duplicated foreign keys.
79

810
- Fix for Bug#33185116, Have method ResultSet.getBoolean() supporting conversion of 'T' and 'F' in a VARCHAR to True/False (boolean).

src/main/core-impl/java/com/mysql/cj/NativeCharsetSettings.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,13 @@ public int configurePreHandshake(boolean reset) {
271271

272272
// error messages are returned according to character_set_results which, at this point, is set from the response packet
273273
this.errorMessageEncoding = getStaticJavaEncodingForCollationIndex(this.sessionCollationIndex);
274-
this.serverSession.getServerVariables().put(CHARACTER_SET_RESULTS, getStaticMysqlCharsetNameForCollationIndex(this.sessionCollationIndex));
274+
275+
String csName = getStaticMysqlCharsetNameForCollationIndex(this.sessionCollationIndex);
276+
this.serverSession.getServerVariables().put(CHARACTER_SET_RESULTS, csName);
277+
278+
this.serverSession.getServerVariables().put(CHARACTER_SET_CLIENT, csName);
279+
this.serverSession.getServerVariables().put(CHARACTER_SET_CONNECTION, csName);
280+
this.serverSession.getServerVariables().put(COLLATION_CONNECTION, getStaticCollationNameForCollationIndex(this.sessionCollationIndex));
275281

276282
return this.sessionCollationIndex;
277283
}
@@ -354,6 +360,17 @@ public void configurePostHandshake(boolean dontCheckServerMatch) {
354360
this.session.sendCommand(getCommandBuilder().buildComQuery(null, "SET NAMES " + sessionCharsetName + sessionCollationClause), false, 0);
355361
this.serverSession.getServerVariables().put(CHARACTER_SET_CLIENT, sessionCharsetName);
356362
this.serverSession.getServerVariables().put(CHARACTER_SET_CONNECTION, sessionCharsetName);
363+
364+
if (sessionCollationClause.length() > 0) {
365+
this.serverSession.getServerVariables().put(COLLATION_CONNECTION, requiredCollation);
366+
} else {
367+
int idx = getCollationIndexForMysqlCharsetName(sessionCharsetName);
368+
if (idx == MYSQL_COLLATION_INDEX_utf8mb4_0900_ai_ci
369+
&& !this.serverSession.getCapabilities().getServerVersion().meetsMinimum(new ServerVersion(8, 0, 1))) {
370+
idx = MYSQL_COLLATION_INDEX_utf8mb4_general_ci;
371+
}
372+
this.serverSession.getServerVariables().put(COLLATION_CONNECTION, getCollationNameForCollationIndex(idx));
373+
}
357374
}
358375
}
359376

@@ -514,6 +531,10 @@ private void buildCollationMapping() {
514531
customCollationNameToCollationIndex = new HashMap<>();
515532
customCollationIndexToCharsetName = new HashMap<>();
516533
customCharsetNameToMblen = new HashMap<>();
534+
customCharsetNameToJavaEncoding = new HashMap<>();
535+
customJavaEncodingUcToCharsetName = new HashMap<>();
536+
customCharsetNameToCollationIndex = new HashMap<>();
537+
customMultibyteEncodings = new HashSet<>();
517538

518539
String customCharsetMapping = this.session.getPropertySet().getStringProperty(PropertyKey.customCharsetMapping).getValue();
519540
if (customCharsetMapping != null) {

src/main/core-impl/java/com/mysql/cj/NativeSession.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.net.SocketAddress;
3737
import java.sql.SQLException;
3838
import java.util.ArrayList;
39+
import java.util.Collections;
3940
import java.util.HashMap;
4041
import java.util.List;
4142
import java.util.Map;
@@ -327,9 +328,7 @@ private void createConfigCacheIfNeeded(Object syncMutex) {
327328
}
328329

329330
try {
330-
Class<?> factoryClass;
331-
332-
factoryClass = Class.forName(getPropertySet().getStringProperty(PropertyKey.serverConfigCacheFactory).getStringValue());
331+
Class<?> factoryClass = Class.forName(getPropertySet().getStringProperty(PropertyKey.serverConfigCacheFactory).getStringValue());
333332

334333
@SuppressWarnings("unchecked")
335334
CacheAdapterFactory<String, Map<String, String>> cacheFactory = ((CacheAdapterFactory<String, Map<String, String>>) factoryClass.newInstance());
@@ -396,8 +395,11 @@ public void loadServerVariables(Object syncMutex, String version) {
396395

397396
if (cachedServerVersion != null && getServerSession().getServerVersion() != null
398397
&& cachedServerVersion.equals(getServerSession().getServerVersion().toString())) {
399-
this.protocol.getServerSession().setServerVariables(cachedVariableMap);
400-
398+
Map<String, String> localVariableMap = this.protocol.getServerSession().getServerVariables();
399+
Map<String, String> newLocalVariableMap = new HashMap<>();
400+
newLocalVariableMap.putAll(cachedVariableMap);
401+
newLocalVariableMap.putAll(localVariableMap); // preserving variables already configured on previous session initialization steps
402+
this.protocol.getServerSession().setServerVariables(newLocalVariableMap);
401403
return;
402404
}
403405

@@ -484,7 +486,9 @@ public void loadServerVariables(Object syncMutex, String version) {
484486

485487
if (this.cacheServerConfiguration.getValue()) {
486488
this.protocol.getServerSession().getServerVariables().put(SERVER_VERSION_STRING_VAR_NAME, getServerSession().getServerVersion().toString());
487-
this.serverConfigCache.put(this.hostInfo.getDatabaseUrl(), this.protocol.getServerSession().getServerVariables());
489+
Map<String, String> localVariableMap = new HashMap<>();
490+
localVariableMap.putAll(this.protocol.getServerSession().getServerVariables());
491+
this.serverConfigCache.put(this.hostInfo.getDatabaseUrl(), Collections.unmodifiableMap(localVariableMap));
488492
}
489493
}
490494

src/test/java/testsuite/regression/CharsetRegressionTest.java

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import static org.junit.jupiter.api.Assumptions.assumeTrue;
3838

3939
import java.io.UnsupportedEncodingException;
40+
import java.lang.reflect.Field;
4041
import java.sql.Connection;
4142
import java.sql.DriverManager;
4243
import java.sql.PreparedStatement;
@@ -52,9 +53,11 @@
5253

5354
import org.junit.jupiter.api.Test;
5455

56+
import com.mysql.cj.CacheAdapter;
5557
import com.mysql.cj.CharsetMappingWrapper;
5658
import com.mysql.cj.CharsetSettings;
5759
import com.mysql.cj.MysqlConnection;
60+
import com.mysql.cj.NativeSession;
5861
import com.mysql.cj.Query;
5962
import com.mysql.cj.ServerVersion;
6063
import com.mysql.cj.conf.PropertyDefinitions.DatabaseTerm;
@@ -1183,4 +1186,128 @@ public <T extends Resultset> T preProcess(Supplier<String> str, Query intercepte
11831186
return null;
11841187
}
11851188
}
1189+
1190+
/**
1191+
* Tests fix for Bug#95139 (29807572), CACHESERVERCONFIGURATION APPEARS TO THWART CHARSET DETECTION.
1192+
*
1193+
* @throws Exception
1194+
*/
1195+
@Test
1196+
public void testBug95139() throws Exception {
1197+
1198+
Properties p = new Properties();
1199+
p.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
1200+
p.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
1201+
p.setProperty(PropertyKey.queryInterceptors.getKeyName(), Bug95139QueryInterceptor.class.getName());
1202+
testBug95139CheckVariables(p, 1, null, "SET " + CharsetSettings.CHARACTER_SET_RESULTS + " = NULL");
1203+
1204+
p.setProperty(PropertyKey.cacheServerConfiguration.getKeyName(), "true");
1205+
p.setProperty(PropertyKey.detectCustomCollations.getKeyName(), "true");
1206+
1207+
// Empty the cache possibly created by other tests to get a correct queryVarsCnt on the next step
1208+
Connection c = getConnectionWithProps(p);
1209+
Field f = NativeSession.class.getDeclaredField("serverConfigCache");
1210+
f.setAccessible(true);
1211+
@SuppressWarnings("unchecked")
1212+
CacheAdapter<String, Map<String, String>> cache = (CacheAdapter<String, Map<String, String>>) f.get(((MysqlConnection) c).getSession());
1213+
if (cache != null) {
1214+
cache.invalidateAll();
1215+
}
1216+
1217+
p.setProperty(PropertyKey.characterEncoding.getKeyName(), "cp1252");
1218+
p.setProperty(PropertyKey.characterSetResults.getKeyName(), "cp1252");
1219+
testBug95139CheckVariables(p, 1, null, null);
1220+
1221+
p.setProperty(PropertyKey.characterEncoding.getKeyName(), "UTF-8");
1222+
p.setProperty(PropertyKey.characterSetResults.getKeyName(), "cp1252");
1223+
testBug95139CheckVariables(p, 0, null, "SET " + CharsetSettings.CHARACTER_SET_RESULTS + " = latin1");
1224+
1225+
p.setProperty(PropertyKey.characterEncoding.getKeyName(), "UTF-8");
1226+
p.remove(PropertyKey.characterSetResults.getKeyName());
1227+
testBug95139CheckVariables(p, 0, null, "SET " + CharsetSettings.CHARACTER_SET_RESULTS + " = NULL");
1228+
1229+
p.setProperty(PropertyKey.characterEncoding.getKeyName(), "UTF-8");
1230+
p.setProperty(PropertyKey.passwordCharacterEncoding.getKeyName(), "latin1");
1231+
testBug95139CheckVariables(p, 0, "SET NAMES utf8mb4", "SET " + CharsetSettings.CHARACTER_SET_RESULTS + " = NULL");
1232+
1233+
p.setProperty(PropertyKey.characterEncoding.getKeyName(), "UTF-8");
1234+
p.setProperty(PropertyKey.passwordCharacterEncoding.getKeyName(), "latin1");
1235+
p.setProperty(PropertyKey.connectionCollation.getKeyName(), "utf8mb4_bin");
1236+
testBug95139CheckVariables(p, 0, "SET NAMES utf8mb4 COLLATE utf8mb4_bin", "SET " + CharsetSettings.CHARACTER_SET_RESULTS + " = NULL");
1237+
}
1238+
1239+
private void testBug95139CheckVariables(Properties p, int queryVarsCnt, String expSetNamesQuery, String expSetCharacterSetResultsquery) throws Exception {
1240+
Connection con = getConnectionWithProps(p);
1241+
1242+
Bug95139QueryInterceptor si = (Bug95139QueryInterceptor) ((JdbcConnection) con).getQueryInterceptorsInstances().get(0);
1243+
assertEquals(queryVarsCnt, si.queryVarsCnt);
1244+
assertEquals(expSetNamesQuery == null ? 0 : 1, si.setNamesCnt);
1245+
assertEquals(expSetCharacterSetResultsquery == null ? 0 : 1, si.setCharacterSetResultsCnt);
1246+
if (expSetNamesQuery != null) {
1247+
assertEquals(expSetNamesQuery, si.setNamesQuery);
1248+
}
1249+
if (expSetCharacterSetResultsquery != null) {
1250+
assertEquals(expSetCharacterSetResultsquery, si.setCharacterSetResultsQuery);
1251+
}
1252+
1253+
Map<String, String> svs = ((MysqlConnection) con).getSession().getServerSession().getServerVariables();
1254+
System.out.println(svs);
1255+
Map<String, String> exp = new HashMap<>();
1256+
exp.put("character_set_client", svs.get("character_set_client"));
1257+
exp.put("character_set_connection", svs.get("character_set_connection"));
1258+
exp.put("character_set_results", svs.get("character_set_results") == null ? "" : svs.get("character_set_results"));
1259+
exp.put("character_set_server", svs.get("character_set_server"));
1260+
exp.put("collation_server", svs.get("collation_server"));
1261+
exp.put("collation_connection", svs.get("collation_connection"));
1262+
1263+
ResultSet rset = con.createStatement()
1264+
.executeQuery("show variables where variable_name='character_set_client' or variable_name='character_set_connection'"
1265+
+ " or variable_name='character_set_results' or variable_name='character_set_server' or variable_name='collation_server'"
1266+
+ " or variable_name='collation_connection'");
1267+
while (rset.next()) {
1268+
System.out.println(rset.getString(1) + "=" + rset.getString(2));
1269+
assertEquals(exp.get(rset.getString(1)), rset.getString(2), rset.getString(1));
1270+
}
1271+
1272+
con.close();
1273+
}
1274+
1275+
public static class Bug95139QueryInterceptor extends BaseQueryInterceptor {
1276+
int queryVarsCnt = 0;
1277+
int setNamesCnt = 0;
1278+
String setNamesQuery = null;
1279+
int setCharacterSetResultsCnt = 0;
1280+
String setCharacterSetResultsQuery = null;
1281+
1282+
@Override
1283+
public <M extends Message> M preProcess(M queryPacket) {
1284+
String sql = StringUtils.toString(queryPacket.getByteBuffer(), 0, queryPacket.getPosition());
1285+
if (sql.contains("SET NAMES")) {
1286+
this.setNamesCnt++;
1287+
this.setNamesQuery = sql.substring(sql.indexOf("SET"));
1288+
} else if (sql.contains("SET " + CharsetSettings.CHARACTER_SET_RESULTS)) {
1289+
this.setCharacterSetResultsCnt++;
1290+
this.setCharacterSetResultsQuery = sql.substring(sql.indexOf("SET"));
1291+
} else if (sql.contains("SHOW VARIABLES") || sql.contains("SELECT @@")) {
1292+
System.out.println(sql.substring(sql.indexOf("S")));
1293+
this.queryVarsCnt++;
1294+
}
1295+
return null;
1296+
}
1297+
1298+
@Override
1299+
public <T extends Resultset> T preProcess(Supplier<String> str, Query interceptedQuery) {
1300+
String sql = str.get();
1301+
if (sql.contains("SET NAMES")) {
1302+
this.setNamesCnt++;
1303+
} else if (sql.contains("SET " + CharsetSettings.CHARACTER_SET_RESULTS)) {
1304+
this.setCharacterSetResultsCnt++;
1305+
} else if (sql.contains("SHOW VARIABLES") || sql.contains("SELECT @@")) {
1306+
System.out.println(sql.substring(sql.indexOf("S")));
1307+
this.queryVarsCnt++;
1308+
}
1309+
return null;
1310+
}
1311+
}
1312+
11861313
}

0 commit comments

Comments
 (0)