From 2e350a5d66cda9317aa308954bafaf952e8054cb Mon Sep 17 00:00:00 2001 From: Alexander Soklakov Date: Mon, 25 Feb 2019 11:52:32 +0400 Subject: [PATCH] Fix for Bug#29329326, PLEASE AVOID SHOW PROCESSLIST IF POSSIBLE. --- CHANGES | 2 + .../com/mysql/cj/protocol/SocketMetadata.java | 42 +++++++++---------- .../java/com/mysql/cj/NativeSession.java | 17 ++++++-- .../cj/LocalizedErrorMessages.properties | 1 - .../regression/ConnectionRegressionTest.java | 41 ++++++++++++++++++ 5 files changed, 77 insertions(+), 26 deletions(-) diff --git a/CHANGES b/CHANGES index 81d917678..790651abd 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,8 @@ Version 8.0.16 + - Fix for Bug#29329326, PLEASE AVOID SHOW PROCESSLIST IF POSSIBLE. + - WL#12460, DevAPI: Support new session reset functionality. - WL#12459, DevAPI: Support connection-attributes. diff --git a/src/main/core-api/java/com/mysql/cj/protocol/SocketMetadata.java b/src/main/core-api/java/com/mysql/cj/protocol/SocketMetadata.java index 5d42acc6d..e5bd79984 100644 --- a/src/main/core-api/java/com/mysql/cj/protocol/SocketMetadata.java +++ b/src/main/core-api/java/com/mysql/cj/protocol/SocketMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -51,38 +51,36 @@ default boolean isLocallyConnected(Session sess, String processHost) { int endIndex = processHost.lastIndexOf(":"); if (endIndex != -1) { processHost = processHost.substring(0, endIndex); + } - try { + try { - InetAddress[] whereMysqlThinksIConnectedFrom = InetAddress.getAllByName(processHost); + InetAddress[] whereMysqlThinksIConnectedFrom = InetAddress.getAllByName(processHost); - SocketAddress remoteSocketAddr = sess.getRemoteSocketAddress(); + SocketAddress remoteSocketAddr = sess.getRemoteSocketAddress(); - if (remoteSocketAddr instanceof InetSocketAddress) { - InetAddress whereIConnectedTo = ((InetSocketAddress) remoteSocketAddr).getAddress(); + if (remoteSocketAddr instanceof InetSocketAddress) { + InetAddress whereIConnectedTo = ((InetSocketAddress) remoteSocketAddr).getAddress(); - for (InetAddress hostAddr : whereMysqlThinksIConnectedFrom) { - if (hostAddr.equals(whereIConnectedTo)) { - sess.getLog().logDebug(Messages.getString("SocketMetadata.1", new Object[] { hostAddr, whereIConnectedTo })); - return true; - } - sess.getLog().logDebug(Messages.getString("SocketMetadata.2", new Object[] { hostAddr, whereIConnectedTo })); + for (InetAddress hostAddr : whereMysqlThinksIConnectedFrom) { + if (hostAddr.equals(whereIConnectedTo)) { + sess.getLog().logDebug(Messages.getString("SocketMetadata.1", new Object[] { hostAddr, whereIConnectedTo })); + return true; } - - } else { - sess.getLog().logDebug(Messages.getString("SocketMetadata.3", new Object[] { remoteSocketAddr })); + sess.getLog().logDebug(Messages.getString("SocketMetadata.2", new Object[] { hostAddr, whereIConnectedTo })); } - return false; - } catch (UnknownHostException e) { - sess.getLog().logWarn(Messages.getString("Connection.CantDetectLocalConnect", new Object[] { processHost }), e); - - return false; + } else { + sess.getLog().logDebug(Messages.getString("SocketMetadata.3", new Object[] { remoteSocketAddr })); } + return false; + } catch (UnknownHostException e) { + sess.getLog().logWarn(Messages.getString("Connection.CantDetectLocalConnect", new Object[] { processHost }), e); + + return false; } - sess.getLog().logWarn(Messages.getString("SocketMetadata.4", new Object[] { processHost })); - return false; + } return false; diff --git a/src/main/core-impl/java/com/mysql/cj/NativeSession.java b/src/main/core-impl/java/com/mysql/cj/NativeSession.java index eb771d0fc..6cde00d83 100644 --- a/src/main/core-impl/java/com/mysql/cj/NativeSession.java +++ b/src/main/core-impl/java/com/mysql/cj/NativeSession.java @@ -530,8 +530,9 @@ public boolean configureClientCharacterSet(boolean dontCheckServerMatch) { this.characterEncoding.setValue(realJavaEncoding); } /* not utf-8 */else { - String mysqlCharsetName = connectionCollationSuffix.length() > 0 ? connectionCollationCharset : CharsetMapping - .getMysqlCharsetForJavaEncoding(realJavaEncoding.toUpperCase(Locale.ENGLISH), getServerSession().getServerVersion()); + String mysqlCharsetName = connectionCollationSuffix.length() > 0 ? connectionCollationCharset + : CharsetMapping.getMysqlCharsetForJavaEncoding(realJavaEncoding.toUpperCase(Locale.ENGLISH), + getServerSession().getServerVersion()); if (mysqlCharsetName != null) { @@ -815,6 +816,7 @@ public void loadServerVariables(Object syncMutex, String version) { queryBuf.append(", @@lower_case_table_names AS lower_case_table_names"); queryBuf.append(", @@max_allowed_packet AS max_allowed_packet"); queryBuf.append(", @@net_write_timeout AS net_write_timeout"); + queryBuf.append(", @@performance_schema AS performance_schema"); if (!versionMeetsMinimum(8, 0, 3)) { queryBuf.append(", @@query_cache_size AS query_cache_size"); queryBuf.append(", @@query_cache_type AS query_cache_type"); @@ -1045,7 +1047,16 @@ private String findProcessHost(long threadId) { try { String processHost = null; - NativePacketPayload resultPacket = sendCommand(this.commandBuilder.buildComQuery(null, "SHOW PROCESSLIST"), false, 0); + String ps = this.protocol.getServerSession().getServerVariable("performance_schema"); + + NativePacketPayload resultPacket = versionMeetsMinimum(5, 6, 0) // performance_schema.threads in MySQL 5.5 does not contain PROCESSLIST_HOST column + && ps != null && ("1".contentEquals(ps) || "ON".contentEquals(ps)) + ? sendCommand(this.commandBuilder.buildComQuery(null, + "select PROCESSLIST_ID, PROCESSLIST_USER, PROCESSLIST_HOST from performance_schema.threads where PROCESSLIST_ID=" + + threadId), + false, 0) + : sendCommand(this.commandBuilder.buildComQuery(null, "SHOW PROCESSLIST"), false, 0); + Resultset rs = ((NativeProtocol) this.protocol).readAllResults(-1, false, resultPacket, false, null, new ResultsetFactory(Type.FORWARD_ONLY, null)); ValueFactory lvf = new LongValueFactory(getPropertySet()); diff --git a/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties b/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties index 922ee233f..3f01141b7 100644 --- a/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties +++ b/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties @@ -536,7 +536,6 @@ SocketMetadata.0=Using 'host' value of ''{0}'' to determine locality of connecti SocketMetadata.1=Locally connected - HostAddress({0}).equals(whereIconnectedTo({1}) SocketMetadata.2=Attempted locally connected check failed - ! HostAddress({0}).equals(whereIconnectedTo({1}) SocketMetadata.3=Remote socket address {0} is not an inet socket address -SocketMetadata.4=No port number present in 'host' from SHOW PROCESSLIST ''{0}'', unable to determine whether locally connected Statement.0=Connection is closed. Statement.2=Unsupported character encoding ''{0}'' diff --git a/src/test/java/testsuite/regression/ConnectionRegressionTest.java b/src/test/java/testsuite/regression/ConnectionRegressionTest.java index a7d553914..e74421af6 100644 --- a/src/test/java/testsuite/regression/ConnectionRegressionTest.java +++ b/src/test/java/testsuite/regression/ConnectionRegressionTest.java @@ -11322,4 +11322,45 @@ public void testBug93007() throws Exception { conn2.close(); } + + /** + * Tests fix for Bug#29329326, PLEASE AVOID SHOW PROCESSLIST IF POSSIBLE. + * + * @throws Exception + */ + public void testBug29329326() throws Exception { + Properties p = new Properties(); + p.setProperty(PropertyKey.queryInterceptors.getKeyName(), Bug29329326QueryInterceptor.class.getName()); + + JdbcConnection c = (JdbcConnection) getConnectionWithProps(p); + Bug29329326QueryInterceptor qi = (Bug29329326QueryInterceptor) c.getQueryInterceptorsInstances().get(0); + assertTrue("SHOW PROCESSLIST was issued during connection establishing", qi.cnt == 0); + + ((com.mysql.cj.jdbc.ConnectionImpl) c).isServerLocal(); + + String ps = ((MysqlConnection) c).getSession().getServerSession().getServerVariable("performance_schema"); + if (versionMeetsMinimum(5, 6, 0) // performance_schema.threads in MySQL 5.5 does not contain PROCESSLIST_HOST column + && ps != null && ("1".contentEquals(ps) || "ON".contentEquals(ps))) { + assertTrue("SHOW PROCESSLIST was issued by isServerLocal()", qi.cnt == 0); + } else { + assertTrue("SHOW PROCESSLIST wasn't issued by isServerLocal()", qi.cnt > 0); + } + } + + /** + * Counts the number of issued "SHOW PROCESSLIST" statements. + */ + public static class Bug29329326QueryInterceptor extends BaseQueryInterceptor { + int cnt = 0; + + @Override + public M preProcess(M queryPacket) { + String sql = StringUtils.toString(queryPacket.getByteBuffer(), 1, (queryPacket.getPosition() - 1)); + if (sql.contains("SHOW PROCESSLIST")) { + this.cnt++; + } + return null; + } + } + }