Skip to content

Commit ce8167a

Browse files
committed
Fix for BUG#87600 (26724154), CONNECTOR THROWS 'MALFORMED DATABASE URL'
ON NON MYSQL CONNECTION-URLS.
1 parent ec9183e commit ce8167a

File tree

18 files changed

+169
-89
lines changed

18 files changed

+169
-89
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.12
55

6+
- Fix for BUG#87600 (26724154), CONNECTOR THROWS 'MALFORMED DATABASE URL' ON NON MYSQL CONNECTION-URLS.
7+
68
- Fix for BUG#26089880, GETCONNECTION("MYSQLX://..") RETURNS NON-X PROTOCOL CONNECTION.
79

810
- WL#11876, Improve connection properties design.

src/main/core-api/java/com/mysql/cj/conf/ConnectionUrl.java

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.mysql.cj.exceptions.CJException;
5151
import com.mysql.cj.exceptions.ExceptionFactory;
5252
import com.mysql.cj.exceptions.InvalidConnectionAttributeException;
53+
import com.mysql.cj.exceptions.UnsupportedConnectionStringException;
5354
import com.mysql.cj.exceptions.WrongArgumentException;
5455
import com.mysql.cj.util.LRUCache;
5556
import com.mysql.cj.util.Util;
@@ -94,7 +95,7 @@ public boolean assertSize(int n) {
9495
}
9596

9697
/**
97-
* The database URL type which is determined by the protocol section of the connection string.
98+
* The database URL type which is determined by the scheme section of the connection string.
9899
*/
99100
public enum Type {
100101
SINGLE_CONNECTION("jdbc:mysql:", HostsCardinality.SINGLE), //
@@ -103,43 +104,61 @@ public enum Type {
103104
REPLICATION_CONNECTION("jdbc:mysql:replication:", HostsCardinality.ONE_OR_MORE), //
104105
XDEVAPI_SESSION("mysqlx:", HostsCardinality.ONE_OR_MORE);
105106

106-
private String protocol;
107+
private String scheme;
107108
private HostsCardinality cardinality;
108109

109-
private Type(String protocol, HostsCardinality cardinality) {
110-
this.protocol = protocol;
110+
private Type(String scheme, HostsCardinality cardinality) {
111+
this.scheme = scheme;
111112
this.cardinality = cardinality;
112113
}
113114

114-
public String getProtocol() {
115-
return this.protocol;
115+
public String getScheme() {
116+
return this.scheme;
116117
}
117118

118119
public HostsCardinality getCardinality() {
119120
return this.cardinality;
120121
}
121122

122123
/**
123-
* Returns the {@link Type} corresponding to the given protocol and number of hosts, if any. Otherwise throws an {@link IllegalArgumentException}.
124-
* Calling this method with the argument n lower than 0 skips the hosts cardinality validation. This should be used for URL protocol validation only as
125-
* the returned {@link Type} won't won't reliable represent the database URL type.
124+
* Returns the {@link Type} corresponding to the given scheme and number of hosts, if any.
125+
* Otherwise throws an {@link UnsupportedConnectionStringException}.
126+
* Calling this method with the argument n lower than 0 skips the hosts cardinality validation.
126127
*
127-
* @param protocol
128-
* the protocol
128+
* @param scheme
129+
* one of supported schemes
129130
* @param n
130131
* the number of hosts in the database URL
131132
* @return the {@link Type} corresponding to the given protocol and number of hosts
132133
*/
133-
public static Type fromValue(String protocol, int n) {
134+
public static Type fromValue(String scheme, int n) {
134135
for (Type t : values()) {
135-
if (t.getProtocol().equalsIgnoreCase(protocol) && (n < 0 || t.getCardinality().assertSize(n))) {
136+
if (t.getScheme().equalsIgnoreCase(scheme) && (n < 0 || t.getCardinality().assertSize(n))) {
136137
return t;
137138
}
138139
}
139140
if (n < 0) {
140-
throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.5", new Object[] { protocol }));
141+
throw ExceptionFactory.createException(UnsupportedConnectionStringException.class,
142+
Messages.getString("ConnectionString.5", new Object[] { scheme }));
141143
}
142-
throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.6", new Object[] { protocol, n }));
144+
throw ExceptionFactory.createException(UnsupportedConnectionStringException.class,
145+
Messages.getString("ConnectionString.6", new Object[] { scheme, n }));
146+
}
147+
148+
/**
149+
* Checks if the given scheme corresponds to one of the connection types the driver supports.
150+
*
151+
* @param scheme
152+
* scheme part from connection string, like "jdbc:mysql:"
153+
* @return true if the given scheme is supported by driver
154+
*/
155+
public static boolean isSupported(String scheme) {
156+
for (Type t : values()) {
157+
if (t.getScheme().equalsIgnoreCase(scheme)) {
158+
return true;
159+
}
160+
}
161+
return false;
143162
}
144163
}
145164

@@ -177,13 +196,6 @@ public static ConnectionUrl getConnectionUrlInstance(String connString, Properti
177196
connectionString = connectionUrlCache.get(connStringCacheKey);
178197
if (connectionString == null) {
179198
ConnectionUrlParser connStrParser = ConnectionUrlParser.parseConnectionString(connString);
180-
try {
181-
Type.fromValue(connStrParser.getScheme(), -1);
182-
} catch (WrongArgumentException e) {
183-
return new ConnectionUrl(connString) {
184-
};
185-
}
186-
187199
switch (Type.fromValue(connStrParser.getScheme(), connStrParser.getHosts().size())) {
188200
case SINGLE_CONNECTION:
189201
connectionString = (ConnectionUrl) Util.getInstance("com.mysql.cj.conf.url.SingleConnectionUrl",
@@ -206,8 +218,7 @@ public static ConnectionUrl getConnectionUrlInstance(String connString, Properti
206218
new Class<?>[] { ConnectionUrlParser.class, Properties.class }, new Object[] { connStrParser, info }, null);
207219
break;
208220
default:
209-
return new ConnectionUrl(connString) {
210-
};
221+
return null; // should not happen
211222
}
212223
connectionUrlCache.put(connStringCacheKey, connectionString);
213224
}
@@ -245,16 +256,7 @@ private static String buildConnectionStringCacheKey(String connString, Propertie
245256
* @return true if this class is able to process the given URL, false otherwise
246257
*/
247258
public static boolean acceptsUrl(String connString) {
248-
if (connString == null) {
249-
throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.0"));
250-
}
251-
try {
252-
ConnectionUrlParser connStringParser = ConnectionUrlParser.parseConnectionString(connString);
253-
Type.fromValue(connStringParser.getScheme(), -1);
254-
} catch (Throwable t) {
255-
return false;
256-
}
257-
return true;
259+
return ConnectionUrlParser.isConnectionStringSupported(connString);
258260
}
259261

260262
/**

src/main/core-api/java/com/mysql/cj/conf/ConnectionUrlParser.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@
4545
import java.util.regex.Pattern;
4646

4747
import com.mysql.cj.Messages;
48+
import com.mysql.cj.conf.ConnectionUrl.Type;
4849
import com.mysql.cj.exceptions.ExceptionFactory;
50+
import com.mysql.cj.exceptions.UnsupportedConnectionStringException;
4951
import com.mysql.cj.exceptions.WrongArgumentException;
5052
import com.mysql.cj.util.StringUtils;
5153

@@ -89,6 +91,7 @@ public class ConnectionUrlParser implements DatabaseUrlContainer {
8991
+ "(?:/(?!\\s*/)(?<path>[^?#]*))?" // path: optional; starts with "/" but not followed by "/", and then followed by by any char except "?" and "#"
9092
+ "(?:\\?(?!\\s*\\?)(?<query>[^#]*))?" // query: optional; starts with "?" but not followed by "?", and then followed by by any char except "#"
9193
+ "(?:\\s*#(?<fragment>.*))?"); // fragment: optional; starts with "#", and then followed by anything
94+
private static final Pattern SCHEME_PTRN = Pattern.compile("(?<scheme>[\\w:%]+).*");
9295
private static final Pattern HOST_LIST_PTRN = Pattern.compile("^\\[(?<hosts>.*)\\]$");
9396
private static final Pattern GENERIC_HOST_PTRN = Pattern.compile("^(?<host>.*?)(?::(?<port>[^:]*))?$");
9497
private static final Pattern KEY_VALUE_HOST_PTRN = Pattern.compile("[,\\s]*(?<key>[\\w\\.\\-\\s%]*)(?:=(?<value>[^,=]*))?");
@@ -112,9 +115,6 @@ public class ConnectionUrlParser implements DatabaseUrlContainer {
112115
* @return an instance of {@link ConnectionUrlParser}
113116
*/
114117
public static ConnectionUrlParser parseConnectionString(String connString) {
115-
if (connString == null) {
116-
throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.0"));
117-
}
118118
return new ConnectionUrlParser(connString);
119119
}
120120

@@ -125,10 +125,33 @@ public static ConnectionUrlParser parseConnectionString(String connString) {
125125
* the connection string to parse
126126
*/
127127
private ConnectionUrlParser(String connString) {
128+
if (connString == null) {
129+
throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.0"));
130+
}
131+
if (!isConnectionStringSupported(connString)) {
132+
throw ExceptionFactory.createException(UnsupportedConnectionStringException.class,
133+
Messages.getString("ConnectionString.17", new String[] { connString }));
134+
}
128135
this.baseConnectionString = connString;
129136
parseConnectionString();
130137
}
131138

139+
/**
140+
* Checks if the scheme part of given connection string matches one of the {@link Type}s supported by Connector/J.
141+
* Throws {@link WrongArgumentException} if connString is null.
142+
*
143+
* @param connString
144+
* connection string
145+
* @return true if supported
146+
*/
147+
public static boolean isConnectionStringSupported(String connString) {
148+
if (connString == null) {
149+
throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.0"));
150+
}
151+
Matcher matcher = SCHEME_PTRN.matcher(connString);
152+
return matcher.matches() && Type.isSupported(decode(matcher.group("scheme")));
153+
}
154+
132155
/**
133156
* Splits the connection string in its main sections.
134157
*/
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program is free software; you can redistribute it and/or modify it under
5+
* the terms of the GNU General Public License, version 2.0, as published by the
6+
* Free Software Foundation.
7+
*
8+
* This program is also distributed with certain software (including but not
9+
* limited to OpenSSL) that is licensed under separate terms, as designated in a
10+
* particular file or component or in included license documentation. The
11+
* authors of MySQL hereby grant you an additional permission to link the
12+
* program and your derivative works with the separately licensed software that
13+
* they have included with MySQL.
14+
*
15+
* Without limiting anything contained in the foregoing, this file, which is
16+
* part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
17+
* version 1.0, a copy of which can be found at
18+
* http://oss.oracle.com/licenses/universal-foss-exception.
19+
*
20+
* This program is distributed in the hope that it will be useful, but WITHOUT
21+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
23+
* for more details.
24+
*
25+
* You should have received a copy of the GNU General Public License along with
26+
* this program; if not, write to the Free Software Foundation, Inc.,
27+
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28+
*/
29+
30+
package com.mysql.cj.exceptions;
31+
32+
public class UnsupportedConnectionStringException extends CJException {
33+
34+
private static final long serialVersionUID = 3991597077197801820L;
35+
36+
public UnsupportedConnectionStringException() {
37+
super();
38+
setSQLState(MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT);
39+
}
40+
41+
public UnsupportedConnectionStringException(String message) {
42+
super(message);
43+
setSQLState(MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT);
44+
}
45+
46+
public UnsupportedConnectionStringException(String message, Throwable cause) {
47+
super(message, cause);
48+
setSQLState(MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT);
49+
}
50+
51+
public UnsupportedConnectionStringException(Throwable cause) {
52+
super(cause);
53+
setSQLState(MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT);
54+
}
55+
56+
public UnsupportedConnectionStringException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
57+
super(message, cause, enableSuppression, writableStackTrace);
58+
setSQLState(MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT);
59+
}
60+
}

src/main/core-impl/java/com/mysql/cj/conf/url/LoadbalanceConnectionUrl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public LoadbalanceConnectionUrl(ConnectionUrlParser connStrParser, Properties in
8181
* the properties common to all hosts
8282
*/
8383
public LoadbalanceConnectionUrl(List<HostInfo> hosts, Map<String, String> properties) {
84-
this.originalConnStr = ConnectionUrl.Type.LOADBALANCE_CONNECTION.getProtocol() + "//**internally_generated**" + System.currentTimeMillis() + "**";
84+
this.originalConnStr = ConnectionUrl.Type.LOADBALANCE_CONNECTION.getScheme() + "//**internally_generated**" + System.currentTimeMillis() + "**";
8585
this.type = ConnectionUrl.Type.LOADBALANCE_CONNECTION;
8686
this.hosts.addAll(hosts);
8787
this.properties.putAll(properties);

src/main/core-impl/java/com/mysql/cj/conf/url/ReplicationConnectionUrl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public ReplicationConnectionUrl(ConnectionUrlParser connStrParser, Properties in
114114
* the properties common to all hosts
115115
*/
116116
public ReplicationConnectionUrl(List<HostInfo> masters, List<HostInfo> slaves, Map<String, String> properties) {
117-
this.originalConnStr = ConnectionUrl.Type.REPLICATION_CONNECTION.getProtocol() + "//**internally_generated**" + System.currentTimeMillis() + "**";
117+
this.originalConnStr = ConnectionUrl.Type.REPLICATION_CONNECTION.getScheme() + "//**internally_generated**" + System.currentTimeMillis() + "**";
118118
this.type = ConnectionUrl.Type.REPLICATION_CONNECTION;
119119
this.hosts.addAll(masters);
120120
this.hosts.addAll(slaves);

src/main/core-impl/java/com/mysql/cj/conf/url/XDevAPIConnectionUrl.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,23 +82,23 @@ public XDevAPIConnectionUrl(ConnectionUrlParser connStrParser, Properties info)
8282
} else {
8383
if (!user.equals(hi.getUser()) || !password.equals(hi.getPassword())) {
8484
throw ExceptionFactory.createException(WrongArgumentException.class,
85-
Messages.getString("ConnectionString.14", new Object[] { Type.XDEVAPI_SESSION.getProtocol() }));
85+
Messages.getString("ConnectionString.14", new Object[] { Type.XDEVAPI_SESSION.getScheme() }));
8686
}
8787
if (hasPriority ^ hi.getHostProperties().containsKey(PRIORITY_PROPERTY_KEY)) {
8888
throw ExceptionFactory.createException(WrongArgumentException.class,
89-
Messages.getString("ConnectionString.15", new Object[] { Type.XDEVAPI_SESSION.getProtocol() }));
89+
Messages.getString("ConnectionString.15", new Object[] { Type.XDEVAPI_SESSION.getScheme() }));
9090
}
9191
}
9292
if (hasPriority) {
9393
try {
9494
int priority = Integer.parseInt(hi.getProperty(PRIORITY_PROPERTY_KEY));
9595
if (priority < 0 || priority > 100) {
9696
throw ExceptionFactory.createException(WrongArgumentException.class,
97-
Messages.getString("ConnectionString.16", new Object[] { Type.XDEVAPI_SESSION.getProtocol() }));
97+
Messages.getString("ConnectionString.16", new Object[] { Type.XDEVAPI_SESSION.getScheme() }));
9898
}
9999
} catch (NumberFormatException e) {
100100
throw ExceptionFactory.createException(WrongArgumentException.class,
101-
Messages.getString("ConnectionString.16", new Object[] { Type.XDEVAPI_SESSION.getProtocol() }));
101+
Messages.getString("ConnectionString.16", new Object[] { Type.XDEVAPI_SESSION.getScheme() }));
102102
}
103103
}
104104
}

src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ ConnectionString.13=Illegal database URL, Host ''{0}'' is duplicated in the comb
132132
ConnectionString.14=Illegal database URL, in a ''{0}'' multi-host connection it is required the same credentials in all hosts.
133133
ConnectionString.15=Illegal database URL, in a ''{0}'' multi-host connection it is required that all or none of the hosts set a "priority" value.
134134
ConnectionString.16=Illegal database URL, in a ''{0}'' multi-host connection the "priority" setting must be a value between 0 and 100.
135+
ConnectionString.17=Connector/J cannot handle a connection string ''{0}''.
135136

136137

137138
ConnectionWrapper.0=Can't set autocommit to 'true' on an XAConnection

src/main/user-api/java/com/mysql/cj/xdevapi/SessionFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public class SessionFactory {
5858
*/
5959
private ConnectionUrl parseUrl(String url) {
6060
ConnectionUrl connUrl = ConnectionUrl.getConnectionUrlInstance(url, null);
61-
if (connUrl.getType() != ConnectionUrl.Type.XDEVAPI_SESSION) {
61+
if (connUrl == null || connUrl.getType() != ConnectionUrl.Type.XDEVAPI_SESSION) {
6262
throw ExceptionFactory.createException(InvalidConnectionAttributeException.class, "Initialization via URL failed for \"" + url + "\"");
6363
}
6464
return connUrl;

src/main/user-impl/java/com/mysql/cj/jdbc/MysqlDataSource.java

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@
4747
import com.mysql.cj.conf.ConnectionUrl;
4848
import com.mysql.cj.conf.PropertyDefinitions;
4949
import com.mysql.cj.conf.RuntimeProperty;
50-
import com.mysql.cj.exceptions.MysqlErrorNumbers;
51-
import com.mysql.cj.jdbc.exceptions.SQLError;
5250

5351
/**
5452
* A JNDI DataSource for a Mysql JDBC connection
@@ -341,7 +339,7 @@ public void setUrl(String url) {
341339
*/
342340
public String getUrl() {
343341
if (!this.explicitUrl) {
344-
StringBuilder sbUrl = new StringBuilder(ConnectionUrl.Type.SINGLE_CONNECTION.getProtocol());
342+
StringBuilder sbUrl = new StringBuilder(ConnectionUrl.Type.SINGLE_CONNECTION.getScheme());
345343
sbUrl.append("//").append(getServerName()).append(":").append(getPort()).append("/").append(getDatabaseName());
346344
return sbUrl.toString();
347345
}
@@ -379,22 +377,12 @@ public String getUser() {
379377
* if an error occurs
380378
*/
381379
protected java.sql.Connection getConnection(Properties props) throws SQLException {
382-
String jdbcUrlToUse = null;
383-
384-
if (!this.explicitUrl) {
385-
jdbcUrlToUse = getUrl();
386-
} else {
387-
jdbcUrlToUse = this.url;
388-
}
380+
String jdbcUrlToUse = this.explicitUrl ? this.url : getUrl();
389381

390382
//
391383
// URL should take precedence over properties
392384
//
393385
ConnectionUrl connUrl = ConnectionUrl.getConnectionUrlInstance(jdbcUrlToUse, null);
394-
if (connUrl.getType() == null) {
395-
throw SQLError.createSQLException(Messages.getString("MysqlDataSource.BadUrl", new Object[] { jdbcUrlToUse }),
396-
MysqlErrorNumbers.SQL_STATE_CONNECTION_FAILURE, null);
397-
}
398386
Properties urlProps = connUrl.getConnectionArgumentsAsProperties();
399387
urlProps.remove(PropertyDefinitions.DBNAME_PROPERTY_KEY);
400388
urlProps.remove(PropertyDefinitions.HOST_PROPERTY_KEY);
@@ -404,15 +392,6 @@ protected java.sql.Connection getConnection(Properties props) throws SQLExceptio
404392
return mysqlDriver.connect(jdbcUrlToUse, props);
405393
}
406394

407-
//
408-
// public boolean isWrapperFor(Class<?> iface) throws SQLException {
409-
// throw SQLError.createSQLFeatureNotSupportedException();
410-
// }
411-
//
412-
// public <T> T unwrap(Class<T> iface) throws SQLException {
413-
// throw SQLError.createSQLFeatureNotSupportedException();
414-
// }
415-
416395
@Override
417396
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
418397
return null;

0 commit comments

Comments
 (0)