com.mysql.cj.NativeCharsetSettings Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mysql-connector-j Show documentation
Show all versions of mysql-connector-j Show documentation
JDBC Type 4 driver for MySQL.
The newest version!
/*
* Copyright (c) 2021, 2024, Oracle and/or its affiliates.
*
* 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 Free Software Foundation.
*
* This program is designed to work with certain software that is licensed under separate terms, as designated in a particular file or component or in
* included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the
* separately licensed software that they have either included with the program or referenced in the documentation.
*
* Without limiting anything contained in the foregoing, this file, which is part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
* version 1.0, a copy of which can be found at http://oss.oracle.com/licenses/universal-foss-exception.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.mysql.cj;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import com.mysql.cj.conf.PropertyKey;
import com.mysql.cj.conf.RuntimeProperty;
import com.mysql.cj.exceptions.ExceptionFactory;
import com.mysql.cj.exceptions.WrongArgumentException;
import com.mysql.cj.protocol.Resultset;
import com.mysql.cj.protocol.Resultset.Type;
import com.mysql.cj.protocol.ServerCapabilities;
import com.mysql.cj.protocol.ServerSession;
import com.mysql.cj.protocol.a.NativeConstants;
import com.mysql.cj.protocol.a.NativeMessageBuilder;
import com.mysql.cj.protocol.a.NativePacketPayload;
import com.mysql.cj.protocol.a.ResultsetFactory;
import com.mysql.cj.result.IntegerValueFactory;
import com.mysql.cj.result.Row;
import com.mysql.cj.result.StringValueFactory;
import com.mysql.cj.result.ValueFactory;
import com.mysql.cj.telemetry.TelemetryAttribute;
import com.mysql.cj.telemetry.TelemetryScope;
import com.mysql.cj.telemetry.TelemetrySpan;
import com.mysql.cj.telemetry.TelemetrySpanName;
import com.mysql.cj.util.StringUtils;
public class NativeCharsetSettings extends CharsetMapping implements CharsetSettings {
private NativeSession session;
private ServerSession serverSession;
public Map collationIndexToCollationName = null;
public Map collationNameToCollationIndex = null;
public Map collationIndexToCharsetName = null;
public Map charsetNameToMblen = null;
public Map charsetNameToJavaEncoding = null;
public Map charsetNameToCollationIndex = null;
public Map javaEncodingUcToCharsetName = null;
public Set multibyteEncodings = null;
private Integer sessionCollationIndex = null;
/**
* What character set is the metadata returned in?
*/
private String metadataEncoding = null;
private int metadataCollationIndex;
/**
* The (Java) encoding used to interpret error messages received from the server.
* We use character_set_results (since MySQL 5.5) if it is not null or UTF-8 otherwise.
*/
private String errorMessageEncoding = "Cp1252"; // to begin with, changes after we talk to the server
protected RuntimeProperty characterEncoding;
protected RuntimeProperty passwordCharacterEncoding;
protected RuntimeProperty characterSetResults;
protected RuntimeProperty connectionCollation;
protected RuntimeProperty cacheServerConfiguration;
/**
* If a CharsetEncoder is required for escaping. Needed for SJIS and related problems with \u00A5.
*/
private boolean requiresEscapingEncoder;
private NativeMessageBuilder commandBuilder = null;
private static final Map> customCollationIndexToCollationNameByUrl = new HashMap<>();
private static final Map> customCollationNameToCollationIndexByUrl = new HashMap<>();
/**
* Actual collation index to mysql charset name map of user defined charsets for given server URLs.
*/
private static final Map> customCollationIndexToCharsetNameByUrl = new HashMap<>();
/**
* Actual mysql charset name to mblen map of user defined charsets for given server URLs.
*/
private static final Map> customCharsetNameToMblenByUrl = new HashMap<>();
private static final Map> customCharsetNameToJavaEncodingByUrl = new HashMap<>();
private static final Map> customCharsetNameToCollationIndexByUrl = new HashMap<>();
private static final Map> customJavaEncodingUcToCharsetNameByUrl = new HashMap<>();
private static final Map> customMultibyteEncodingsByUrl = new HashMap<>();
/**
* Does the character set of this connection match the character set of the platform
*/
private boolean platformDbCharsetMatches = true; // changed once we've connected.
private NativeMessageBuilder getCommandBuilder() {
if (this.commandBuilder == null) {
this.commandBuilder = new NativeMessageBuilder(this.serverSession.supportsQueryAttributes());
}
return this.commandBuilder;
}
/**
* Determines if the connection charset is the same as the platform charset
*/
private void checkForCharsetMismatch() {
String characterEncodingValue = this.characterEncoding.getValue();
if (characterEncodingValue != null) {
Charset characterEncodingCs = Charset.forName(characterEncodingValue);
Charset encodingToCheck = Charset.defaultCharset();
this.platformDbCharsetMatches = encodingToCheck.equals(characterEncodingCs);
}
}
@Override
public boolean doesPlatformDbCharsetMatches() {
return this.platformDbCharsetMatches;
}
public NativeCharsetSettings(NativeSession sess) {
this.session = sess;
this.serverSession = this.session.getServerSession();
this.characterEncoding = sess.getPropertySet().getStringProperty(PropertyKey.characterEncoding);
this.characterSetResults = this.session.getPropertySet().getProperty(PropertyKey.characterSetResults);
this.passwordCharacterEncoding = this.session.getPropertySet().getStringProperty(PropertyKey.passwordCharacterEncoding);
this.connectionCollation = this.session.getPropertySet().getStringProperty(PropertyKey.connectionCollation);
this.cacheServerConfiguration = sess.getPropertySet().getBooleanProperty(PropertyKey.cacheServerConfiguration);
tryAndFixEncoding(this.characterEncoding, true);
tryAndFixEncoding(this.passwordCharacterEncoding, true);
if (!"null".equalsIgnoreCase(this.characterSetResults.getValue())) { // the "null" instead of an encoding name is allowed for characterSetResults
tryAndFixEncoding(this.characterSetResults, false);
}
}
/**
* Attempt to use the encoding, and bail out if it can't be used.
*
* @param encodingProperty
* connection property containing the Java encoding to try
* @param replaceImpermissibleEncodings
* The character_set_client system variable cannot be set to ucs2, utf16, utf16le, utf32 charsets. If "true" the corresponding connection
* property value will be replaced with "UTF-8"
*/
private void tryAndFixEncoding(RuntimeProperty encodingProperty, boolean replaceImpermissibleEncodings) {
String oldEncoding = encodingProperty.getValue();
if (oldEncoding != null) {
if (replaceImpermissibleEncodings && ("UnicodeBig".equalsIgnoreCase(oldEncoding) || "UTF-16".equalsIgnoreCase(oldEncoding)
|| "UTF-16LE".equalsIgnoreCase(oldEncoding) || "UTF-32".equalsIgnoreCase(oldEncoding))) {
encodingProperty.setValue("UTF-8");
} else {
try {
StringUtils.getBytes("abc", oldEncoding);
} catch (WrongArgumentException waEx) {
// Try the MySQL character set name, then....
String newEncoding = getStaticJavaEncodingForMysqlCharset(oldEncoding);
if (newEncoding == null) {
throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("StringUtils.0", new Object[] { oldEncoding }),
this.session.getExceptionInterceptor());
}
StringUtils.getBytes("abc", newEncoding);
encodingProperty.setValue(newEncoding);
}
}
}
}
@Override
public int configurePreHandshake(boolean reset) {
if (reset) {
this.sessionCollationIndex = null;
}
// Avoid the second execution of this method
if (this.sessionCollationIndex != null) {
return this.sessionCollationIndex;
}
ServerCapabilities capabilities = this.serverSession.getCapabilities();
String encoding = this.passwordCharacterEncoding.getStringValue();
if (encoding == null) {
String connectionColl = this.connectionCollation.getStringValue();
if ((connectionColl == null || (this.sessionCollationIndex = getStaticCollationIndexForCollationName(connectionColl)) == null)
&& (encoding = this.characterEncoding.getValue()) == null) {
// If none of "passwordCharacterEncoding", "connectionCollation" or "characterEncoding" is specified then use UTF-8.
// It would be better to use the server default collation here, to avoid unnecessary SET NAMES queries after the handshake if server
// default charset if not utf8, but we can not do it until server Bug#32729185 is fixed. Server cuts collation index to lower byte and, for
// example, if the server is started with character-set-server=utf8mb4 and collation-server=utf8mb4_is_0900_ai_ci (collation index 257) the
// Protocol::HandshakeV10 will contain character_set=1, "big5_chinese_ci". This is true not only for MySQL 8.0, where built-in collations with
// indexes > 255 were first introduced, but also other server series would be affected when configured with custom collations, for which the
// reserved collation id range is >= 1024.
this.sessionCollationIndex = MYSQL_COLLATION_INDEX_utf8mb4_0900_ai_ci;
}
}
if (this.sessionCollationIndex == null) {
if ((this.sessionCollationIndex = getStaticCollationIndexForJavaEncoding(encoding, capabilities.getServerVersion())) == 0) {
throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("StringUtils.0", new Object[] { encoding }));
}
}
if (this.sessionCollationIndex > Constants.UNSIGNED_BYTE_MAX_VALUE //
|| isStaticImpermissibleCollation(this.sessionCollationIndex)) { // At this point, impermissible charset can be set only with "connectionCollation".
this.sessionCollationIndex = MYSQL_COLLATION_INDEX_utf8mb4_0900_ai_ci;
}
if (this.sessionCollationIndex == MYSQL_COLLATION_INDEX_utf8mb4_0900_ai_ci
&& !capabilities.getServerVersion().meetsMinimum(new ServerVersion(8, 0, 1))) {
this.sessionCollationIndex = MYSQL_COLLATION_INDEX_utf8mb4_general_ci; // use utf8mb4_general_ci instead of utf8mb4_0900_ai_ci for old servers
}
// error messages are returned according to character_set_results which, at this point, is set from the response packet
this.errorMessageEncoding = getStaticJavaEncodingForCollationIndex(this.sessionCollationIndex);
String csName = getStaticMysqlCharsetNameForCollationIndex(this.sessionCollationIndex);
this.serverSession.getServerVariables().put(CHARACTER_SET_RESULTS, csName);
this.serverSession.getServerVariables().put(CHARACTER_SET_CLIENT, csName);
this.serverSession.getServerVariables().put(CHARACTER_SET_CONNECTION, csName);
this.serverSession.getServerVariables().put(COLLATION_CONNECTION, getStaticCollationNameForCollationIndex(this.sessionCollationIndex));
return this.sessionCollationIndex;
}
@Override
public void configurePostHandshake(boolean dontCheckServerMatch) {
buildCollationMapping();
/*
* Configuring characterEncoding.
*/
String requiredCollation = this.connectionCollation.getStringValue();
String requiredEncoding = this.characterEncoding.getValue();
String passwordEncoding = this.passwordCharacterEncoding.getValue();
Integer requiredCollationIndex;
String sessionCharsetName = getServerDefaultCharset();
String sessionCollationClause = "";
try {
// connectionCollation overrides the characterEncoding value
if (requiredCollation != null && (requiredCollationIndex = getCollationIndexForCollationName(requiredCollation)) != null) {
if (isImpermissibleCollation(requiredCollationIndex)) {
if (this.serverSession.getCapabilities().getServerVersion().meetsMinimum(new ServerVersion(8, 0, 1))) {
requiredCollationIndex = MYSQL_COLLATION_INDEX_utf8mb4_0900_ai_ci;
requiredCollation = "utf8mb4_0900_ai_ci";
} else {
requiredCollationIndex = MYSQL_COLLATION_INDEX_utf8mb4_general_ci;
requiredCollation = "utf8mb4_general_ci";
}
}
sessionCollationClause = " COLLATE " + requiredCollation;
sessionCharsetName = getMysqlCharsetNameForCollationIndex(requiredCollationIndex);
requiredEncoding = getJavaEncodingForCollationIndex(requiredCollationIndex, requiredEncoding);
this.sessionCollationIndex = requiredCollationIndex;
}
if (requiredEncoding != null) { // If either connectionCollation or characterEncoding is defined.
if (sessionCollationClause.length() == 0) { // If no connectionCollation is defined.
sessionCharsetName = getMysqlCharsetForJavaEncoding(requiredEncoding.toUpperCase(Locale.ENGLISH), this.serverSession.getServerVersion());
}
} else { // Neither connectionCollation nor characterEncoding are defined.
// Collations with index > 255 don't fit into server greeting packet.
// Now we can set sessionCollationIndex according to "collation_server" value.
if (!StringUtils.isNullOrEmpty(passwordEncoding)) {
if (this.serverSession.getCapabilities().getServerVersion().meetsMinimum(new ServerVersion(8, 0, 1))) {
this.sessionCollationIndex = MYSQL_COLLATION_INDEX_utf8mb4_0900_ai_ci; // We can't do more, just trying to use utf8mb4_0900_ai_ci because the most of collations in that range are utf8mb4.
requiredCollation = "utf8mb4_0900_ai_ci";
} else {
this.sessionCollationIndex = MYSQL_COLLATION_INDEX_utf8mb4_general_ci;
requiredCollation = "utf8mb4_general_ci";
}
sessionCollationClause = " COLLATE " + getCollationNameForCollationIndex(this.sessionCollationIndex);
}
if ((requiredEncoding = getJavaEncodingForCollationIndex(this.sessionCollationIndex, requiredEncoding)) == null) {
// if there is no mapping for default collation index leave characterEncoding as specified by user
throw ExceptionFactory.createException(Messages.getString("Connection.5", new Object[] { this.sessionCollationIndex.toString() }),
this.session.getExceptionInterceptor());
}
sessionCharsetName = getMysqlCharsetNameForCollationIndex(this.sessionCollationIndex);
}
} catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
throw ExceptionFactory.createException(Messages.getString("Connection.6", new Object[] { this.sessionCollationIndex }),
this.session.getExceptionInterceptor());
}
this.characterEncoding.setValue(requiredEncoding);
if (sessionCharsetName != null) {
boolean isCharsetDifferent = !characterSetNamesMatches(sessionCharsetName);
boolean isCollationDifferent = sessionCollationClause.length() > 0
&& !requiredCollation.equalsIgnoreCase(this.serverSession.getServerVariable(COLLATION_CONNECTION));
if (dontCheckServerMatch || isCharsetDifferent || isCollationDifferent) {
String sql = "SET NAMES " + sessionCharsetName + sessionCollationClause;
telemetryWrapSendCommand(() -> this.session.getProtocol().sendCommand(getCommandBuilder().buildComQuery(null, this.session, sql), false, 0),
TelemetrySpanName.SET_CHARSET);
this.serverSession.getServerVariables().put(CHARACTER_SET_CLIENT, sessionCharsetName);
this.serverSession.getServerVariables().put(CHARACTER_SET_CONNECTION, sessionCharsetName);
if (sessionCollationClause.length() > 0) {
this.serverSession.getServerVariables().put(COLLATION_CONNECTION, requiredCollation);
} else {
int idx = getCollationIndexForMysqlCharsetName(sessionCharsetName);
if (idx == MYSQL_COLLATION_INDEX_utf8mb4_0900_ai_ci
&& !this.serverSession.getCapabilities().getServerVersion().meetsMinimum(new ServerVersion(8, 0, 1))) {
idx = MYSQL_COLLATION_INDEX_utf8mb4_general_ci;
}
this.serverSession.getServerVariables().put(COLLATION_CONNECTION, getCollationNameForCollationIndex(idx));
}
}
}
/*
* Configuring characterSetResults.
*
* We know how to deal with any charset coming back from the database, so tell the server not to do conversion
* if the user hasn't 'forced' a result-set character set.
*/
String sessionResultsCharset = this.serverSession.getServerVariable(CHARACTER_SET_RESULTS);
String characterSetResultsValue = this.characterSetResults.getValue();
if (StringUtils.isNullOrEmpty(characterSetResultsValue) || "null".equalsIgnoreCase(characterSetResultsValue)) {
if (!StringUtils.isNullOrEmpty(sessionResultsCharset) && !"NULL".equalsIgnoreCase(sessionResultsCharset)) {
telemetryWrapSendCommand(() -> this.session.getProtocol()
.sendCommand(getCommandBuilder().buildComQuery(null, this.session, "SET character_set_results = NULL"), false, 0),
TelemetrySpanName.SET_VARIABLE, "character_set_results");
this.serverSession.getServerVariables().put(CHARACTER_SET_RESULTS, null);
}
String defaultMetadataCharsetMysql = this.serverSession.getServerVariable("character_set_system");
this.metadataEncoding = defaultMetadataCharsetMysql != null ? getJavaEncodingForMysqlCharset(defaultMetadataCharsetMysql) : "UTF-8";
this.errorMessageEncoding = "UTF-8";
} else {
String resultsCharsetName = getMysqlCharsetForJavaEncoding(characterSetResultsValue.toUpperCase(Locale.ENGLISH),
this.serverSession.getServerVersion());
if (resultsCharsetName == null) {
throw ExceptionFactory.createException(WrongArgumentException.class,
Messages.getString("Connection.7", new Object[] { characterSetResultsValue }), this.session.getExceptionInterceptor());
}
if (!resultsCharsetName.equalsIgnoreCase(sessionResultsCharset)) {
telemetryWrapSendCommand(
() -> this.session.getProtocol().sendCommand(
getCommandBuilder().buildComQuery(null, this.session, "SET character_set_results = " + resultsCharsetName), false, 0),
TelemetrySpanName.SET_VARIABLE, "character_set_results");
this.serverSession.getServerVariables().put(CHARACTER_SET_RESULTS, resultsCharsetName);
}
this.metadataEncoding = characterSetResultsValue;
this.errorMessageEncoding = characterSetResultsValue;
}
this.metadataCollationIndex = getCollationIndexForJavaEncoding(this.metadataEncoding, this.serverSession.getServerVersion());
checkForCharsetMismatch();
/**
* Check if we need a CharsetEncoder for escaping codepoints that are
* transformed to backslash (0x5c) in the connection encoding.
*/
try {
CharsetEncoder enc = Charset.forName(this.characterEncoding.getValue()).newEncoder();
CharBuffer cbuf = CharBuffer.allocate(1);
ByteBuffer bbuf = ByteBuffer.allocate(1);
cbuf.put("\u00a5");
cbuf.position(0);
enc.encode(cbuf, bbuf, true);
if (bbuf.get(0) == '\\') {
this.requiresEscapingEncoder = true;
} else {
cbuf.clear();
bbuf.clear();
cbuf.put("\u20a9");
cbuf.position(0);
enc.encode(cbuf, bbuf, true);
if (bbuf.get(0) == '\\') {
this.requiresEscapingEncoder = true;
}
}
} catch (java.nio.charset.UnsupportedCharsetException ucex) {
// fallback to String API
byte bbuf[] = StringUtils.getBytes("\u00a5", this.characterEncoding.getValue());
if (bbuf[0] == '\\') {
this.requiresEscapingEncoder = true;
} else {
bbuf = StringUtils.getBytes("\u20a9", this.characterEncoding.getValue());
if (bbuf[0] == '\\') {
this.requiresEscapingEncoder = true;
}
}
}
}
private void telemetryWrapSendCommand(Runnable command, TelemetrySpanName spanName, Object... args) {
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(spanName, args);
try (TelemetryScope scope = span.makeCurrent()) {
span.setAttribute(TelemetryAttribute.DB_NAME, this.session.getHostInfo().getDatabase());
span.setAttribute(TelemetryAttribute.DB_OPERATION, TelemetryAttribute.OPERATION_SET);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, TelemetryAttribute.OPERATION_SET + TelemetryAttribute.STATEMENT_SUFFIX);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, this.session.getHostInfo().getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
command.run();
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
}
private boolean characterSetNamesMatches(String mysqlEncodingName) {
boolean res = false;
if (mysqlEncodingName != null) {
// set names is equivalent to character_set_client ..._results and ..._connection, but we set _results later, so don't check it here.
res = mysqlEncodingName.equalsIgnoreCase(this.serverSession.getServerVariable(CHARACTER_SET_CLIENT))
&& mysqlEncodingName.equalsIgnoreCase(this.serverSession.getServerVariable(CHARACTER_SET_CONNECTION));
// if still doesn't match then checking aliases
List aliases;
if (!res && (aliases = CharsetMapping.getStaticMysqlCharsetAliasesByName(mysqlEncodingName)) != null) {
for (String alias : aliases) {
if (res = alias.equalsIgnoreCase(this.serverSession.getServerVariable(CHARACTER_SET_CLIENT))
&& alias.equalsIgnoreCase(this.serverSession.getServerVariable(CHARACTER_SET_CONNECTION))) {
break;
}
}
}
}
return res;
}
/**
* Get the server's default character set name according to collation index from server greeting,
* or value of 'character_set_server' variable if there is no mapping for that index
*
* @return MySQL charset name
*/
public String getServerDefaultCharset() {
String charset = getStaticMysqlCharsetNameForCollationIndex(this.sessionCollationIndex);
return charset != null ? charset : this.serverSession.getServerVariable("character_set_server");
}
@Override
public String getErrorMessageEncoding() {
return this.errorMessageEncoding;
}
@Override
public String getMetadataEncoding() {
return this.metadataEncoding;
}
@Override
public int getMetadataCollationIndex() {
return this.metadataCollationIndex;
}
@Override
public boolean getRequiresEscapingEncoder() {
return this.requiresEscapingEncoder;
}
@Override
public String getPasswordCharacterEncoding() {
return getStaticJavaEncodingForCollationIndex(this.sessionCollationIndex);
}
/**
* Builds the map needed for 4.1.0 and newer servers that maps field-level
* charset/collation info to a java character encoding name.
*/
private void buildCollationMapping() {
Map customCollationIndexToCollationName = null;
Map customCollationNameToCollationIndex = null;
Map customCollationIndexToCharsetName = null;
Map customCharsetNameToMblen = null;
Map customCharsetNameToJavaEncoding = new HashMap<>();
Map customJavaEncodingUcToCharsetName = new HashMap<>();
Map customCharsetNameToCollationIndex = new HashMap<>();
Set customMultibyteEncodings = new HashSet<>();
String databaseURL = this.session.getHostInfo().getDatabaseUrl();
if (this.cacheServerConfiguration.getValue()) {
synchronized (customCollationIndexToCharsetNameByUrl) {
customCollationIndexToCollationName = customCollationIndexToCollationNameByUrl.get(databaseURL);
customCollationNameToCollationIndex = customCollationNameToCollationIndexByUrl.get(databaseURL);
customCollationIndexToCharsetName = customCollationIndexToCharsetNameByUrl.get(databaseURL);
customCharsetNameToMblen = customCharsetNameToMblenByUrl.get(databaseURL);
customCharsetNameToJavaEncoding = customCharsetNameToJavaEncodingByUrl.get(databaseURL);
customJavaEncodingUcToCharsetName = customJavaEncodingUcToCharsetNameByUrl.get(databaseURL);
customCharsetNameToCollationIndex = customCharsetNameToCollationIndexByUrl.get(databaseURL);
customMultibyteEncodings = customMultibyteEncodingsByUrl.get(databaseURL);
}
}
if (customCollationIndexToCharsetName == null && this.session.getPropertySet().getBooleanProperty(PropertyKey.detectCustomCollations).getValue()) {
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.LOAD_COLLATIONS);
try (TelemetryScope scope = span.makeCurrent()) {
span.setAttribute(TelemetryAttribute.DB_NAME, this.session.getHostInfo().getDatabase());
span.setAttribute(TelemetryAttribute.DB_OPERATION, TelemetryAttribute.OPERATION_SELECT);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, TelemetryAttribute.OPERATION_SELECT + TelemetryAttribute.STATEMENT_SUFFIX);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, this.session.getHostInfo().getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
customCollationIndexToCollationName = new HashMap<>();
customCollationNameToCollationIndex = new HashMap<>();
customCollationIndexToCharsetName = new HashMap<>();
customCharsetNameToMblen = new HashMap<>();
customCharsetNameToJavaEncoding = new HashMap<>();
customJavaEncodingUcToCharsetName = new HashMap<>();
customCharsetNameToCollationIndex = new HashMap<>();
customMultibyteEncodings = new HashSet<>();
String customCharsetMapping = this.session.getPropertySet().getStringProperty(PropertyKey.customCharsetMapping).getValue();
if (customCharsetMapping != null) {
String[] pairs = customCharsetMapping.split(",");
for (String pair : pairs) {
int keyEnd = pair.indexOf(":");
if (keyEnd > 0 && keyEnd + 1 < pair.length()) {
String charset = pair.substring(0, keyEnd);
String encoding = pair.substring(keyEnd + 1);
customCharsetNameToJavaEncoding.put(charset, encoding);
customJavaEncodingUcToCharsetName.put(encoding.toUpperCase(Locale.ENGLISH), charset);
}
}
}
ValueFactory ivf = new IntegerValueFactory(this.session.getPropertySet());
try {
NativePacketPayload resultPacket = this.session.getProtocol().sendCommand(getCommandBuilder().buildComQuery(null, this.session,
"SELECT c.COLLATION_NAME, c.CHARACTER_SET_NAME, c.ID, cs.MAXLEN, c.IS_DEFAULT='Yes' from INFORMATION_SCHEMA.COLLATIONS AS c LEFT JOIN"
+ " INFORMATION_SCHEMA.CHARACTER_SETS AS cs ON cs.CHARACTER_SET_NAME=c.CHARACTER_SET_NAME"),
false, 0);
Resultset rs = this.session.getProtocol().readAllResults(-1, false, resultPacket, false, null,
new ResultsetFactory(Type.FORWARD_ONLY, null));
ValueFactory svf = new StringValueFactory(this.session.getPropertySet());
Row r;
while ((r = rs.getRows().next()) != null) {
String collationName = r.getValue(0, svf);
String charsetName = r.getValue(1, svf);
int collationIndex = ((Number) r.getValue(2, ivf)).intValue();
int maxlen = ((Number) r.getValue(3, ivf)).intValue();
boolean isDefault = ((Number) r.getValue(4, ivf)).intValue() > 0;
if (collationIndex >= MAP_SIZE //
|| collationIndex != getStaticCollationIndexForCollationName(collationName)
|| !charsetName.equals(getStaticMysqlCharsetNameForCollationIndex(collationIndex))) {
customCollationIndexToCollationName.put(collationIndex, collationName);
customCollationNameToCollationIndex.put(collationName, collationIndex);
customCollationIndexToCharsetName.put(collationIndex, charsetName);
if (isDefault || !customCharsetNameToCollationIndex.containsKey(charsetName)
&& CharsetMapping.getStaticCollationIndexForMysqlCharsetName(charsetName) == 0) {
customCharsetNameToCollationIndex.put(charsetName, collationIndex);
}
}
// if no static map for charsetName adding to custom map
if (getStaticMysqlCharsetByName(charsetName) == null) {
customCharsetNameToMblen.put(charsetName, maxlen);
if (maxlen > 1) {
String enc = customCharsetNameToJavaEncoding.get(charsetName);
if (enc != null) {
customMultibyteEncodings.add(enc.toUpperCase(Locale.ENGLISH));
}
}
}
}
} catch (IOException e) {
throw ExceptionFactory.createException(e.getMessage(), e, this.session.getExceptionInterceptor());
}
if (this.cacheServerConfiguration.getValue()) {
synchronized (customCollationIndexToCharsetNameByUrl) {
customCollationIndexToCollationNameByUrl.put(databaseURL, Collections.unmodifiableMap(customCollationIndexToCollationName));
customCollationNameToCollationIndexByUrl.put(databaseURL, Collections.unmodifiableMap(customCollationNameToCollationIndex));
customCollationIndexToCharsetNameByUrl.put(databaseURL, Collections.unmodifiableMap(customCollationIndexToCharsetName));
customCharsetNameToMblenByUrl.put(databaseURL, Collections.unmodifiableMap(customCharsetNameToMblen));
customCharsetNameToJavaEncodingByUrl.put(databaseURL, Collections.unmodifiableMap(customCharsetNameToJavaEncoding));
customJavaEncodingUcToCharsetNameByUrl.put(databaseURL, Collections.unmodifiableMap(customJavaEncodingUcToCharsetName));
customCharsetNameToCollationIndexByUrl.put(databaseURL, Collections.unmodifiableMap(customCharsetNameToCollationIndex));
customMultibyteEncodingsByUrl.put(databaseURL, Collections.unmodifiableSet(customMultibyteEncodings));
}
}
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
}
if (customCollationIndexToCharsetName != null) {
this.collationIndexToCollationName = customCollationIndexToCollationName;
this.collationNameToCollationIndex = customCollationNameToCollationIndex;
this.collationIndexToCharsetName = customCollationIndexToCharsetName;
this.charsetNameToMblen = customCharsetNameToMblen;
this.charsetNameToJavaEncoding = customCharsetNameToJavaEncoding;
this.javaEncodingUcToCharsetName = customJavaEncodingUcToCharsetName;
this.charsetNameToCollationIndex = customCharsetNameToCollationIndex;
this.multibyteEncodings = customMultibyteEncodings;
}
}
@Override
public Integer getCollationIndexForCollationName(String collationName) {
Integer collationIndex = null;
if (this.collationNameToCollationIndex == null || (collationIndex = this.collationNameToCollationIndex.get(collationName)) == null) {
collationIndex = getStaticCollationIndexForCollationName(collationName);
}
return collationIndex;
}
@Override
public String getCollationNameForCollationIndex(Integer collationIndex) {
String collationName = null;
if (collationIndex != null
&& (this.collationIndexToCollationName == null || (collationName = this.collationIndexToCollationName.get(collationIndex)) == null)) {
collationName = getStaticCollationNameForCollationIndex(collationIndex);
}
return collationName;
}
@Override
public String getMysqlCharsetNameForCollationIndex(Integer collationIndex) {
String charset = null;
if (this.collationIndexToCharsetName == null || (charset = this.collationIndexToCharsetName.get(collationIndex)) == null) {
charset = getStaticMysqlCharsetNameForCollationIndex(collationIndex);
}
return charset;
}
@Override
public String getJavaEncodingForCollationIndex(int collationIndex) {
return getJavaEncodingForCollationIndex(collationIndex, this.characterEncoding.getValue());
}
public String getJavaEncodingForCollationIndex(Integer collationIndex, String fallBackJavaEncoding) {
String encoding = null;
String charset = null;
if (collationIndex != NativeConstants.NO_CHARSET_INFO) {
if (this.collationIndexToCharsetName != null && (charset = this.collationIndexToCharsetName.get(collationIndex)) != null) {
encoding = getJavaEncodingForMysqlCharset(charset, fallBackJavaEncoding);
}
if (encoding == null) {
encoding = getStaticJavaEncodingForCollationIndex(collationIndex, fallBackJavaEncoding);
}
}
return encoding != null ? encoding : fallBackJavaEncoding;
}
@Override
public int getCollationIndexForJavaEncoding(String javaEncoding, ServerVersion version) {
return getCollationIndexForMysqlCharsetName(getMysqlCharsetForJavaEncoding(javaEncoding, version));
}
@Override
public int getCollationIndexForMysqlCharsetName(String charsetName) {
Integer index = null;
if (this.charsetNameToCollationIndex == null || (index = this.charsetNameToCollationIndex.get(charsetName)) == null) {
index = getStaticCollationIndexForMysqlCharsetName(charsetName);
}
return index;
}
@Override
public String getJavaEncodingForMysqlCharset(String mysqlCharsetName) {
String encoding = null;
if (this.charsetNameToJavaEncoding == null || (encoding = this.charsetNameToJavaEncoding.get(mysqlCharsetName)) == null) {
encoding = getStaticJavaEncodingForMysqlCharset(mysqlCharsetName);
}
return encoding;
}
public String getJavaEncodingForMysqlCharset(String mysqlCharsetName, String javaEncoding) {
String encoding = null;
if (this.charsetNameToJavaEncoding == null || (encoding = this.charsetNameToJavaEncoding.get(mysqlCharsetName)) == null) {
encoding = getStaticJavaEncodingForMysqlCharset(mysqlCharsetName, javaEncoding);
}
return encoding;
}
@Override
public String getMysqlCharsetForJavaEncoding(String javaEncoding, ServerVersion version) {
String charset = null;
if (this.javaEncodingUcToCharsetName == null || (charset = this.javaEncodingUcToCharsetName.get(javaEncoding.toUpperCase(Locale.ENGLISH))) == null) {
charset = getStaticMysqlCharsetForJavaEncoding(javaEncoding, version);
}
return charset;
}
public boolean isImpermissibleCollation(int collationIndex) {
String charsetName = null;
if (this.collationIndexToCharsetName != null && (charsetName = this.collationIndexToCharsetName.get(collationIndex)) != null) {
if (charsetName.equals(MYSQL_CHARSET_NAME_ucs2) || charsetName.equals(MYSQL_CHARSET_NAME_utf16) || charsetName.equals(MYSQL_CHARSET_NAME_utf16le)
|| charsetName.equals(MYSQL_CHARSET_NAME_utf32)) {
return true;
}
}
return isStaticImpermissibleCollation(collationIndex);
}
@Override
public boolean isMultibyteCharset(String javaEncodingName) {
if (this.multibyteEncodings != null && this.multibyteEncodings.contains(javaEncodingName.toUpperCase(Locale.ENGLISH))) {
return true;
}
return isStaticMultibyteCharset(javaEncodingName);
}
@Override
public int getMaxBytesPerChar(String javaCharsetName) {
return getMaxBytesPerChar(null, javaCharsetName);
}
@Override
public int getMaxBytesPerChar(Integer charsetIndex, String javaCharsetName) {
String charset = null;
if ((charset = getMysqlCharsetNameForCollationIndex(charsetIndex)) == null) {
// if we didn't find charset name by index
charset = getStaticMysqlCharsetForJavaEncoding(javaCharsetName, this.serverSession.getServerVersion());
}
Integer mblen = null;
if (this.charsetNameToMblen == null || (mblen = this.charsetNameToMblen.get(charset)) == null) {
mblen = getStaticMblen(charset);
}
return mblen != null ? mblen.intValue() : 1;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy