This is an automated email from the ASF dual-hosted git repository.
zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new cb1652a866c proxy, parser: honor character_set_results for SET NAMES
(#38186)
cb1652a866c is described below
commit cb1652a866ce8296010bee537d0902816619bb0a
Author: Aviral Garg <[email protected]>
AuthorDate: Fri Feb 27 15:26:57 2026 +0530
proxy, parser: honor character_set_results for SET NAMES (#38186)
* proxy, parser: honor character_set_results for SET NAMES
* proxy: skip charset update for character_set_results NULL
---
.../statement/type/DorisDALStatementVisitor.java | 30 ++++++++++++++++-----
.../statement/type/MySQLDALStatementVisitor.java | 30 ++++++++++++++++-----
.../variable/charset/CharsetSetExecutor.java | 3 ++-
.../variable/charset/CharsetVariableProvider.java | 11 ++++++++
.../variable/charset/CharsetSetExecutorTest.java | 31 ++++++++++++++++++++++
.../charset/MySQLCharsetVariableProvider.java | 7 ++++-
.../MySQLSetVariableAdminExecutorTest.java | 24 +++++++++++++++++
.../charset/MySQLCharsetVariableProviderTest.java | 16 +++++++++--
test/it/parser/src/main/resources/case/dal/set.xml | 12 +++++++++
9 files changed, 148 insertions(+), 16 deletions(-)
diff --git
a/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/type/DorisDALStatementVisitor.java
b/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/type/DorisDALStatementVisitor.java
index 63275446874..e4356ab16d4 100644
---
a/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/type/DorisDALStatementVisitor.java
+++
b/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/type/DorisDALStatementVisitor.java
@@ -945,10 +945,32 @@ public final class DorisDALStatementVisitor extends
DorisStatementVisitor implem
private List<VariableAssignSegment> getVariableAssigns(final
OptionValueListContext ctx) {
List<VariableAssignSegment> result = new LinkedList<>();
- result.add(null == ctx.optionValueNoOptionType() ?
getVariableAssignSegment(ctx) :
getVariableAssignSegment(ctx.optionValueNoOptionType()));
+ if (null == ctx.optionValueNoOptionType()) {
+ result.add(getVariableAssignSegment(ctx));
+ } else {
+
result.addAll(getVariableAssignSegments(ctx.optionValueNoOptionType()));
+ }
for (OptionValueContext each : ctx.optionValue()) {
- result.add(getVariableAssignSegment(each));
+ if (null == each.optionValueNoOptionType()) {
+ result.add(getVariableAssignSegment(each));
+ } else {
+
result.addAll(getVariableAssignSegments(each.optionValueNoOptionType()));
+ }
+ }
+ return result;
+ }
+
+ private List<VariableAssignSegment> getVariableAssignSegments(final
OptionValueNoOptionTypeContext ctx) {
+ if (null == ctx.NAMES()) {
+ return Collections.singletonList(getVariableAssignSegment(ctx));
}
+ int startIndex = ctx.start.getStartIndex();
+ int stopIndex = ctx.stop.getStopIndex();
+ String assignValue = ctx.charsetName().getText();
+ List<VariableAssignSegment> result = new LinkedList<>();
+ result.add(new VariableAssignSegment(startIndex, stopIndex, new
VariableSegment(startIndex, stopIndex, "character_set_client"), assignValue));
+ result.add(new VariableAssignSegment(startIndex, stopIndex, new
VariableSegment(startIndex, stopIndex, "character_set_results"), assignValue));
+ result.add(new VariableAssignSegment(startIndex, stopIndex, new
VariableSegment(startIndex, stopIndex, "character_set_connection"),
assignValue));
return result;
}
@@ -972,10 +994,6 @@ public final class DorisDALStatementVisitor extends
DorisStatementVisitor implem
}
private VariableSegment getVariableSegment(final
OptionValueNoOptionTypeContext ctx) {
- if (null != ctx.NAMES()) {
- // TODO Consider setting all three system variables:
character_set_client, character_set_results, character_set_connection
- return new
VariableSegment(ctx.NAMES().getSymbol().getStartIndex(),
ctx.NAMES().getSymbol().getStopIndex(), "character_set_client");
- }
if (null != ctx.internalVariableName()) {
return new
VariableSegment(ctx.internalVariableName().start.getStartIndex(),
ctx.internalVariableName().stop.getStopIndex(),
ctx.internalVariableName().getText());
}
diff --git
a/parser/sql/engine/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/engine/mysql/visitor/statement/type/MySQLDALStatementVisitor.java
b/parser/sql/engine/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/engine/mysql/visitor/statement/type/MySQLDALStatementVisitor.java
index ff8ee6e6f52..068d0a1991d 100644
---
a/parser/sql/engine/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/engine/mysql/visitor/statement/type/MySQLDALStatementVisitor.java
+++
b/parser/sql/engine/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/engine/mysql/visitor/statement/type/MySQLDALStatementVisitor.java
@@ -788,13 +788,35 @@ public final class MySQLDALStatementVisitor extends
MySQLStatementVisitor implem
private List<VariableAssignSegment> getVariableAssigns(final
OptionValueListContext ctx) {
List<VariableAssignSegment> result = new LinkedList<>();
- result.add(null == ctx.optionValueNoOptionType() ?
getVariableAssignSegment(ctx) :
getVariableAssignSegment(ctx.optionValueNoOptionType()));
+ if (null == ctx.optionValueNoOptionType()) {
+ result.add(getVariableAssignSegment(ctx));
+ } else {
+
result.addAll(getVariableAssignSegments(ctx.optionValueNoOptionType()));
+ }
for (OptionValueContext each : ctx.optionValue()) {
- result.add(getVariableAssignSegment(each));
+ if (null == each.optionValueNoOptionType()) {
+ result.add(getVariableAssignSegment(each));
+ } else {
+
result.addAll(getVariableAssignSegments(each.optionValueNoOptionType()));
+ }
}
return result;
}
+ private List<VariableAssignSegment> getVariableAssignSegments(final
OptionValueNoOptionTypeContext ctx) {
+ if (null == ctx.NAMES()) {
+ return Collections.singletonList(getVariableAssignSegment(ctx));
+ }
+ int startIndex = ctx.start.getStartIndex();
+ int stopIndex = ctx.stop.getStopIndex();
+ String assignValue = ctx.charsetName().getText();
+ List<VariableAssignSegment> result = new LinkedList<>();
+ result.add(new VariableAssignSegment(startIndex, stopIndex, new
VariableSegment(startIndex, stopIndex, "character_set_client"), assignValue));
+ result.add(new VariableAssignSegment(startIndex, stopIndex, new
VariableSegment(startIndex, stopIndex, "character_set_results"), assignValue));
+ result.add(new VariableAssignSegment(startIndex, stopIndex, new
VariableSegment(startIndex, stopIndex, "character_set_connection"),
assignValue));
+ return result;
+ }
+
private VariableAssignSegment getVariableAssignSegment(final
OptionValueContext ctx) {
if (null != ctx.optionValueNoOptionType()) {
return getVariableAssignSegment(ctx.optionValueNoOptionType());
@@ -815,10 +837,6 @@ public final class MySQLDALStatementVisitor extends
MySQLStatementVisitor implem
}
private VariableSegment getVariableSegment(final
OptionValueNoOptionTypeContext ctx) {
- if (null != ctx.NAMES()) {
- // TODO Consider setting all three system variables:
character_set_client, character_set_results, character_set_connection
- return new
VariableSegment(ctx.NAMES().getSymbol().getStartIndex(),
ctx.NAMES().getSymbol().getStopIndex(), "character_set_client");
- }
if (null != ctx.internalVariableName()) {
return new
VariableSegment(ctx.internalVariableName().start.getStartIndex(),
ctx.internalVariableName().stop.getStopIndex(),
ctx.internalVariableName().getText());
}
diff --git
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetSetExecutor.java
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetSetExecutor.java
index 416fc741513..f7779d0dabf 100644
---
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetSetExecutor.java
+++
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetSetExecutor.java
@@ -43,7 +43,8 @@ public final class CharsetSetExecutor {
*/
public void set(final String variableName, final String toBeAssignedValue)
{
Optional<CharsetVariableProvider> charsetVariableProvider =
DatabaseTypedSPILoader.findService(CharsetVariableProvider.class, databaseType);
- if (charsetVariableProvider.isPresent() &&
charsetVariableProvider.get().getCharsetVariables().stream().anyMatch(each ->
each.equalsIgnoreCase(variableName))) {
+ if (charsetVariableProvider.isPresent() &&
charsetVariableProvider.get().getCharsetVariables().stream().anyMatch(each ->
each.equalsIgnoreCase(variableName))
+ && charsetVariableProvider.get().shouldSet(variableName,
toBeAssignedValue)) {
connectionSession.getAttributeMap().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).set(charsetVariableProvider.get().parseCharset(toBeAssignedValue));
}
}
diff --git
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetVariableProvider.java
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetVariableProvider.java
index 5bbff920ff5..052ed079545 100644
---
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetVariableProvider.java
+++
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetVariableProvider.java
@@ -36,6 +36,17 @@ public interface CharsetVariableProvider extends
DatabaseTypedSPI {
*/
Collection<String> getCharsetVariables();
+ /**
+ * Determine whether charset should be set.
+ *
+ * @param variableName variable name
+ * @param variableValue variable value
+ * @return true if charset should be set
+ */
+ default boolean shouldSet(final String variableName, final String
variableValue) {
+ return true;
+ }
+
/**
* Parse charset from variable.
*
diff --git
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetSetExecutorTest.java
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetSetExecutorTest.java
index e7fdce73818..5bb4982060f 100644
---
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetSetExecutorTest.java
+++
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/variable/charset/CharsetSetExecutorTest.java
@@ -34,6 +34,7 @@ import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -48,6 +49,7 @@ class CharsetSetExecutorTest {
void assertSetWhenProviderPresentAndVariableMatched() {
CharsetVariableProvider provider = mock(CharsetVariableProvider.class);
when(provider.getCharsetVariables()).thenReturn(Collections.singletonList("character_set_client"));
+ when(provider.shouldSet("CHARACTER_SET_CLIENT",
"utf8")).thenReturn(true);
when(provider.parseCharset("utf8")).thenReturn(StandardCharsets.UTF_8);
when(DatabaseTypedSPILoader.findService(CharsetVariableProvider.class,
databaseType)).thenReturn(Optional.of(provider));
AttributeMap attributeMap = mock(AttributeMap.class);
@@ -72,4 +74,33 @@ class CharsetSetExecutorTest {
when(DatabaseTypedSPILoader.findService(CharsetVariableProvider.class,
databaseType)).thenReturn(Optional.of(provider));
assertDoesNotThrow(() -> new CharsetSetExecutor(databaseType,
mock()).set("character_set_client", "utf8"));
}
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ @Test
+ void assertSetWhenCharacterSetResultsMatched() {
+ CharsetVariableProvider provider = mock(CharsetVariableProvider.class);
+
when(provider.getCharsetVariables()).thenReturn(Collections.singletonList("character_set_results"));
+ when(provider.shouldSet("CHARACTER_SET_RESULTS",
"utf8")).thenReturn(true);
+ when(provider.parseCharset("utf8")).thenReturn(StandardCharsets.UTF_8);
+ when(DatabaseTypedSPILoader.findService(CharsetVariableProvider.class,
databaseType)).thenReturn(Optional.of(provider));
+ AttributeMap attributeMap = mock(AttributeMap.class);
+ Attribute attribute = mock(Attribute.class);
+
when(attributeMap.attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(attribute);
+ ConnectionSession connectionSession = mock(ConnectionSession.class);
+ when(connectionSession.getAttributeMap()).thenReturn(attributeMap);
+ new CharsetSetExecutor(databaseType,
connectionSession).set("CHARACTER_SET_RESULTS", "utf8");
+ verify(attribute).set(StandardCharsets.UTF_8);
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ @Test
+ void assertSetWhenProviderRejectsVariableValue() {
+ CharsetVariableProvider provider = mock(CharsetVariableProvider.class);
+
when(provider.getCharsetVariables()).thenReturn(Collections.singletonList("character_set_results"));
+ when(provider.shouldSet("CHARACTER_SET_RESULTS",
"NULL")).thenReturn(false);
+ when(DatabaseTypedSPILoader.findService(CharsetVariableProvider.class,
databaseType)).thenReturn(Optional.of(provider));
+ ConnectionSession connectionSession = mock(ConnectionSession.class);
+ new CharsetSetExecutor(databaseType,
connectionSession).set("CHARACTER_SET_RESULTS", "NULL");
+ verify(connectionSession, never()).getAttributeMap();
+ }
}
diff --git
a/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProvider.java
b/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProvider.java
index cd3344b6dca..0b364373a98 100644
---
a/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProvider.java
+++
b/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProvider.java
@@ -35,7 +35,12 @@ public final class MySQLCharsetVariableProvider implements
CharsetVariableProvid
@Override
public Collection<String> getCharsetVariables() {
- return Arrays.asList("charset", "character_set_client");
+ return Arrays.asList("charset", "character_set_client",
"character_set_results", "character_set_connection");
+ }
+
+ @Override
+ public boolean shouldSet(final String variableName, final String
variableValue) {
+ return !"character_set_results".equalsIgnoreCase(variableName) ||
!"null".equalsIgnoreCase(formatValue(variableValue));
}
@Override
diff --git
a/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutorTest.java
b/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutorTest.java
index 7c99e034ef9..00135383c3f 100644
---
a/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutorTest.java
+++
b/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutorTest.java
@@ -77,6 +77,30 @@ class MySQLSetVariableAdminExecutorTest {
assertThat(connectionSession.getAttributeMap().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).get(),
is(StandardCharsets.UTF_8));
}
+ @Test
+ void assertExecuteWithCharacterSetResults() throws SQLException {
+ SetStatement setStatement = new SetStatement(databaseType,
Collections.singletonList(
+ new VariableAssignSegment(0, 0, new VariableSegment(0, 0,
"character_set_results"), "'utf8mb4'")));
+ MySQLSetVariableAdminExecutor executor = new
MySQLSetVariableAdminExecutor(setStatement);
+ ConnectionSession connectionSession = mock(ConnectionSession.class);
+ when(connectionSession.getAttributeMap()).thenReturn(new
DefaultAttributeMap());
+ executor.execute(connectionSession, mock());
+
assertThat(connectionSession.getAttributeMap().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).get(),
is(StandardCharsets.UTF_8));
+ }
+
+ @Test
+ void assertExecuteWithCharacterSetResultsAsNull() throws SQLException {
+ SetStatement setStatement = new SetStatement(databaseType,
Collections.singletonList(
+ new VariableAssignSegment(0, 0, new VariableSegment(0, 0,
"character_set_results"), "NULL")));
+ MySQLSetVariableAdminExecutor executor = new
MySQLSetVariableAdminExecutor(setStatement);
+ ConnectionSession connectionSession = mock(ConnectionSession.class);
+ DefaultAttributeMap attributeMap = new DefaultAttributeMap();
+
attributeMap.attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).set(StandardCharsets.UTF_8);
+ when(connectionSession.getAttributeMap()).thenReturn(attributeMap);
+ executor.execute(connectionSession, mock());
+
assertThat(connectionSession.getAttributeMap().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).get(),
is(StandardCharsets.UTF_8));
+ }
+
private ConnectionContext mockConnectionContext() {
ConnectionContext result = mock(ConnectionContext.class);
when(result.getCurrentDatabaseName()).thenReturn(Optional.of("foo_db"));
diff --git
a/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProviderTest.java
b/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProviderTest.java
index 1ab31b9bf02..919f452ed0d 100644
---
a/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProviderTest.java
+++
b/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProviderTest.java
@@ -35,6 +35,8 @@ import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;
class MySQLCharsetVariableProviderTest {
@@ -45,7 +47,7 @@ class MySQLCharsetVariableProviderTest {
@Test
void assertCharsetVariablesAndDatabaseType() {
- assertThat(provider.getCharsetVariables(), contains("charset",
"character_set_client"));
+ assertThat(provider.getCharsetVariables(), contains("charset",
"character_set_client", "character_set_results", "character_set_connection"));
}
@Test
@@ -53,7 +55,17 @@ class MySQLCharsetVariableProviderTest {
assertThrows(UnknownCharsetException.class, () ->
provider.parseCharset("unknown_charset"));
}
- @ParameterizedTest
+ @Test
+ void assertShouldSetWhenCharacterSetResultsWithNull() {
+ assertFalse(provider.shouldSet("character_set_results", "NULL"));
+ }
+
+ @Test
+ void assertShouldSetWhenCharacterSetResultsWithCharset() {
+ assertTrue(provider.shouldSet("character_set_results", "'utf8mb4'"));
+ }
+
+ @ParameterizedTest(name = "{0}")
@MethodSource("successArguments")
void assertParseCharset(final String input, final Charset expected) {
assertThat(provider.parseCharset(input), is(expected));
diff --git a/test/it/parser/src/main/resources/case/dal/set.xml
b/test/it/parser/src/main/resources/case/dal/set.xml
index 0ef64b40479..61ba695d862 100644
--- a/test/it/parser/src/main/resources/case/dal/set.xml
+++ b/test/it/parser/src/main/resources/case/dal/set.xml
@@ -99,6 +99,12 @@
<parameter-assign value="'utf8'">
<parameter name="character_set_client" />
</parameter-assign>
+ <parameter-assign value="'utf8'">
+ <parameter name="character_set_results" />
+ </parameter-assign>
+ <parameter-assign value="'utf8'">
+ <parameter name="character_set_connection" />
+ </parameter-assign>
</set-parameter>
<set-parameter sql-case-id="set_character_set_doris">
<parameter-assign value="utf8mb4">
@@ -112,6 +118,12 @@
<parameter-assign value="'utf8'">
<parameter name="character_set_client" />
</parameter-assign>
+ <parameter-assign value="'utf8'">
+ <parameter name="character_set_results" />
+ </parameter-assign>
+ <parameter-assign value="'utf8'">
+ <parameter name="character_set_connection" />
+ </parameter-assign>
</set-parameter>
<set-parameter sql-case-id="set_charset_postgresql">
<parameter-assign value="'UTF8'">