Skip to content

Commit

Permalink
[CONJ-1199] adding option connectionCollation in order to set utf8mb4…
Browse files Browse the repository at this point in the history
… collation that differ from default utf8mb4 collation
  • Loading branch information
rusher committed Oct 7, 2024
1 parent ded788f commit 9b69c21
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 4 deletions.
47 changes: 47 additions & 0 deletions src/main/java/org/mariadb/jdbc/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public class Configuration {

// various
private String timezone = null;
private String connectionCollation = null;
private String connectionTimeZone = null;
private Boolean forceConnectionTimeZoneToSession = null;
private boolean preserveInstants;
Expand Down Expand Up @@ -174,6 +175,7 @@ private Configuration(
Properties nonMappedOptions,
String timezone,
String connectionTimeZone,
String connectionCollation,
boolean forceConnectionTimeZoneToSession,
boolean preserveInstants,
Boolean autocommit,
Expand Down Expand Up @@ -261,6 +263,7 @@ private Configuration(
this.nonMappedOptions = nonMappedOptions;
this.timezone = timezone;
this.connectionTimeZone = connectionTimeZone;
this.connectionCollation = connectionCollation;
this.forceConnectionTimeZoneToSession = forceConnectionTimeZoneToSession;
this.preserveInstants = preserveInstants;
this.autocommit = autocommit;
Expand Down Expand Up @@ -378,6 +381,7 @@ private Configuration(
Boolean yearIsDateType,
String timezone,
String connectionTimeZone,
String connectionCollation,
Boolean forceConnectionTimeZoneToSession,
Boolean preserveInstants,
Boolean dumpQueriesOnException,
Expand Down Expand Up @@ -475,6 +479,7 @@ private Configuration(
if (yearIsDateType != null) this.yearIsDateType = yearIsDateType;
this.timezone = timezone;
if (connectionTimeZone != null) this.connectionTimeZone = connectionTimeZone;
if (connectionCollation != null) this.connectionCollation = connectionCollation;
if (forceConnectionTimeZoneToSession != null)
this.forceConnectionTimeZoneToSession = forceConnectionTimeZoneToSession;
if (preserveInstants != null) this.preserveInstants = preserveInstants;
Expand Down Expand Up @@ -618,6 +623,24 @@ private Configuration(
// option value verification
// *************************************************************

// ensure connection collation format
if (connectionCollation != null) {
if ("".equals(connectionCollation.trim())) {
this.connectionCollation = null;
} else {
// ensure this is an utf8mb4 collation
if (!connectionCollation.toLowerCase(Locale.ROOT).startsWith("utf8mb4_")) {
throw new SQLException(
String.format(
"wrong connection collation '%s' only utf8mb4 collation are accepted",
connectionCollation));
} else if (!connectionCollation.matches("^[a-zA-Z0-9_]+$")) {
throw new SQLException(
String.format("wrong connection collation '%s' name", connectionCollation));
}
}
}

// int fields must all be positive
Field[] fields = Configuration.class.getDeclaredFields();
try {
Expand Down Expand Up @@ -651,6 +674,7 @@ public Builder toBuilder() {
.haMode(this.haMode)
.timezone(this.timezone)
.connectionTimeZone(this.connectionTimeZone)
.connectionCollation(this.connectionCollation)
.forceConnectionTimeZoneToSession(this.forceConnectionTimeZoneToSession)
.preserveInstants(this.preserveInstants)
.autocommit(this.autocommit)
Expand Down Expand Up @@ -1640,6 +1664,15 @@ public String connectionTimeZone() {
return connectionTimeZone;
}

/**
* get connectionCollation
*
* @return connectionCollation
*/
public String connectionCollation() {
return connectionCollation;
}

/**
* forceConnectionTimeZoneToSession must connection timezone be forced
*
Expand Down Expand Up @@ -2129,6 +2162,7 @@ public static final class Builder implements Cloneable {
// various
private String timezone;
private String connectionTimeZone;
private String connectionCollation;
private Boolean forceConnectionTimeZoneToSession;
private Boolean preserveInstants;
private Boolean autocommit;
Expand Down Expand Up @@ -2808,6 +2842,18 @@ public Builder connectionTimeZone(String connectionTimeZone) {
return this;
}

/**
* indicate what utf8mb4 collation to use. if not set, server default collation for utf8mb4 will
* be used
*
* @param connectionCollation utf8mb4 collation to use
* @return this {@link Builder}
*/
public Builder connectionCollation(String connectionCollation) {
this.connectionCollation = nullOrEmpty(connectionCollation);
return this;
}

/**
* Indicate if connectionTimeZone must be forced to session
*
Expand Down Expand Up @@ -3350,6 +3396,7 @@ public Configuration build() throws SQLException {
this.yearIsDateType,
this.timezone,
this.connectionTimeZone,
this.connectionCollation,
this.forceConnectionTimeZoneToSession,
this.preserveInstants,
this.dumpQueriesOnException,
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/org/mariadb/jdbc/client/impl/StandardClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -817,9 +817,16 @@ public String createSessionVariableQuery(Context context) {
context.canUseTransactionIsolation() ? "transaction_read_only" : "tx_read_only"));
}

if (context.getCharset() == null || !"utf8mb4".equals(context.getCharset())) {
sessionCommands.add("NAMES utf8mb4");
if (context.getCharset() == null
|| !"utf8mb4".equals(context.getCharset())
|| conf.connectionCollation() != null) {
String defaultCharsetSet = "NAMES utf8mb4";
if (conf.connectionCollation() != null) {
defaultCharsetSet += " COLLATE " + conf.connectionCollation();
}
sessionCommands.add(defaultCharsetSet);
}

if (!sessionCommands.isEmpty()) {
return "set " + sessionCommands.stream().collect(Collectors.joining(","));
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/driver.properties
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,5 @@ nullDatabaseMeansCurrent=When enable, in DatabaseMetadata, will handle null data
preserveInstants= This option controls whether the connector converts Timestamp values to the connection's time zone.
connectionTimeZone=This option defines the connection's time zone. LOCAL retrieves the JVM's default time zone, SERVER fetches the server's global time zone upon connection creation, and <user-defined time zone> allows specifying a server time zone without requesting it during connection establishment.
forceConnectionTimeZoneToSession=This setting dictates whether the connector enforces the connection time zone for the session.
pinGlobalTxToPhysicalConnection=When set, will reuse previous connection used for XID.
pinGlobalTxToPhysicalConnection=When set, will reuse previous connection used for XID.
connectionCollation=indicate what utf8mb4 collation to use. if not set, server default collation for utf8mb4 will be used
34 changes: 34 additions & 0 deletions src/test/java/org/mariadb/jdbc/integration/ConfigurationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,38 @@ public void jdbcCompliantTruncation() throws SQLException {
stmt.execute("SET @@global.sql_mode = '" + sqlMode + "'");
}
}

@Test
public void connectionCollationTest() throws SQLException {
try (org.mariadb.jdbc.Connection conn =
createCon("&connectionCollation=utf8mb4_vietnamese_ci")) {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT @@session.COLLATION_CONNECTION");
rs.next();
assertEquals("utf8mb4_vietnamese_ci", rs.getString(1));
}

try (org.mariadb.jdbc.Connection conn = createCon("&connectionCollation=")) {
Statement stmt = conn.createStatement();
ResultSet rs =
stmt.executeQuery("SELECT @@global.COLLATION_CONNECTION, @@session.COLLATION_CONNECTION");
rs.next();
assertEquals(rs.getString(2), rs.getString(1));
}

Statement stmt = sharedConn.createStatement();
ResultSet rs =
stmt.executeQuery("SELECT @@global.COLLATION_CONNECTION, @@session.COLLATION_CONNECTION");
rs.next();
assertEquals(rs.getString(2), rs.getString(1));

assertThrowsContains(
SQLException.class,
() -> createCon("&connectionCollation=utf8_vietnamese_ci"),
"wrong connection collation 'utf8_vietnamese_ci' only utf8mb4 collation are accepted");
assertThrowsContains(
SQLException.class,
() -> createCon("&connectionCollation=utf8mb4_vietnamese_ci;SELECT"),
"wrong connection collation 'utf8mb4_vietnamese_ci;SELECT' name");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -904,9 +904,10 @@ public void builder() throws SQLException {
.preserveInstants(true)
.connectionTimeZone("SERVER")
.forceConnectionTimeZoneToSession(false)
.connectionCollation("utf8mb4_vietnamese_ci")
.build();
String expected =
"jdbc:mariadb://host1:3305,address=(host=host2)(port=3307)(type=replica)/db?user=me&password=***&timezone=UTC&connectionTimeZone=SERVER&forceConnectionTimeZoneToSession=false&preserveInstants=true&autocommit=false&nullDatabaseMeansCurrent=true&useCatalogTerm=SCHEMA&createDatabaseIfNotExist=true&useLocalSessionState=true&returnMultiValuesGeneratedIds=true&permitRedirect=false&transactionIsolation=REPEATABLE_READ&defaultFetchSize=10&maxQuerySizeToLog=100&maxAllowedPacket=8000&geometryDefaultType=default&restrictedAuth=mysql_native_password,client_ed25519&initSql=SET"
"jdbc:mariadb://host1:3305,address=(host=host2)(port=3307)(type=replica)/db?user=me&password=***&timezone=UTC&connectionCollation=utf8mb4_vietnamese_ci&connectionTimeZone=SERVER&forceConnectionTimeZoneToSession=false&preserveInstants=true&autocommit=false&nullDatabaseMeansCurrent=true&useCatalogTerm=SCHEMA&createDatabaseIfNotExist=true&useLocalSessionState=true&returnMultiValuesGeneratedIds=true&permitRedirect=false&transactionIsolation=REPEATABLE_READ&defaultFetchSize=10&maxQuerySizeToLog=100&maxAllowedPacket=8000&geometryDefaultType=default&restrictedAuth=mysql_native_password,client_ed25519&initSql=SET"
+ " @@a='10'&socketFactory=someSocketFactory&connectTimeout=22&uuidAsString=true&tcpKeepAlive=false&tcpKeepIdle=10&tcpKeepCount=50&tcpKeepInterval=50&tcpAbortiveClose=true&localSocketAddress=localSocketAddress&socketTimeout=1000&useReadAheadInput=true&tlsSocketType=TLStype&sslMode=TRUST&serverSslCert=mycertPath&keyStore=/tmp&keyStorePassword=MyPWD&keyStoreType=JKS&trustStoreType=JKS&enabledSslCipherSuites=myCipher,cipher2&enabledSslProtocolSuites=TLSv1.2&fallbackToSystemKeyStore=false&fallbackToSystemTrustStore=false&allowMultiQueries=true&allowLocalInfile=false&useCompression=true&useAffectedRows=true&useBulkStmts=true&disablePipeline=true&cachePrepStmts=false&prepStmtCacheSize=2&useServerPrepStmts=true&credentialType=ENV&sessionVariables=blabla&connectionAttributes=bla=bla&servicePrincipalName=SPN&blankTableNameMeta=true&tinyInt1isBit=false&yearIsDateType=false&dumpQueriesOnException=true&includeInnodbStatusInDeadlockExceptions=true&includeThreadDumpInDeadlockExceptions=true&retriesAllDown=10&galeraAllowedState=A,B&transactionReplay=true&pool=true&poolName=myPool&maxPoolSize=16&minPoolSize=12&maxIdleTime=25000&registerJmxPool=false&poolValidMinDelay=260&useResetConnection=true&serverRsaPublicKeyFile=RSAPath&allowPublicKeyRetrieval=true";
assertEquals(expected, conf.toString());
assertEquals(expected, conf.toBuilder().build().toString());
Expand Down

0 comments on commit 9b69c21

Please sign in to comment.