This is an automated email from the ASF dual-hosted git repository.

duanzhengqiang 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 d03e3cec8ed Support select distinct(column) encrypt rewrite and 
refactor SubstitutableColumnNameToken build logic (#32344)
d03e3cec8ed is described below

commit d03e3cec8ed84d0f32bb97ba3b38e24e394f6264
Author: Zhengqiang Duan <[email protected]>
AuthorDate: Wed Jul 31 18:44:38 2024 +0800

    Support select distinct(column) encrypt rewrite and refactor 
SubstitutableColumnNameToken build logic (#32344)
    
    * Support select distinct(column) encrypt rewrite and refactor 
SubstitutableColumnNameToken build logic
    
    * remove useless param
---
 .../EncryptIndexColumnTokenGenerator.java          |  4 +-
 .../EncryptProjectionTokenGenerator.java           | 81 ++++++++++++++--------
 .../select/projection/engine/ProjectionEngine.java |  3 +-
 .../select/projection/impl/ColumnProjection.java   | 29 +++++++-
 .../expression/type/ColumnSegmentBinder.java       |  3 +-
 .../pojo/generic/SubstitutableColumnNameToken.java |  6 +-
 .../visitor/statement/DorisStatementVisitor.java   |  4 +-
 .../visitor/statement/MySQLStatementVisitor.java   |  6 +-
 .../core/segment/dml/column/ColumnSegment.java     | 25 ++++++-
 .../core/segment/generic/ParenthesesSegment.java   |  4 +-
 .../asserts/segment/generic/ParenthesesAssert.java |  2 +-
 .../segment/projection/ProjectionAssert.java       | 27 ++++++--
 .../segment/impl/generic/ExpectedParentheses.java  |  4 +-
 .../impl/column/ExpectedColumnProjection.java      |  9 +--
 .../parser/src/main/resources/case/dml/select.xml  |  9 ++-
 .../sql/supported/dml/select-distinct.xml          |  4 +-
 .../dml/select/select-distinct.xml                 | 12 ++++
 17 files changed, 169 insertions(+), 63 deletions(-)

diff --git 
a/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rewrite/token/generator/EncryptIndexColumnTokenGenerator.java
 
b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rewrite/token/generator/EncryptIndexColumnTokenGenerator.java
index bd7631bf68c..6baef50288f 100644
--- 
a/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rewrite/token/generator/EncryptIndexColumnTokenGenerator.java
+++ 
b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rewrite/token/generator/EncryptIndexColumnTokenGenerator.java
@@ -22,15 +22,15 @@ import lombok.Setter;
 import org.apache.shardingsphere.encrypt.rewrite.aware.DatabaseTypeAware;
 import org.apache.shardingsphere.encrypt.rewrite.aware.EncryptRuleAware;
 import org.apache.shardingsphere.encrypt.rule.EncryptRule;
-import org.apache.shardingsphere.encrypt.rule.table.EncryptTable;
 import org.apache.shardingsphere.encrypt.rule.column.EncryptColumn;
+import org.apache.shardingsphere.encrypt.rule.table.EncryptTable;
 import 
org.apache.shardingsphere.infra.binder.context.segment.select.projection.Projection;
 import 
org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.ColumnProjection;
 import 
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
 import org.apache.shardingsphere.infra.binder.context.type.IndexAvailable;
 import org.apache.shardingsphere.infra.binder.context.type.TableAvailable;
-import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
 import 
org.apache.shardingsphere.infra.database.core.metadata.database.enums.QuoteCharacter;
+import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
 import 
org.apache.shardingsphere.infra.rewrite.sql.token.generator.CollectionSQLTokenGenerator;
 import org.apache.shardingsphere.infra.rewrite.sql.token.pojo.SQLToken;
 import 
org.apache.shardingsphere.infra.rewrite.sql.token.pojo.generic.SubstitutableColumnNameToken;
diff --git 
a/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rewrite/token/generator/projection/EncryptProjectionTokenGenerator.java
 
b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rewrite/token/generator/projection/EncryptProjectionTokenGenerator.java
index 69ec9740e0d..2aadf69e135 100644
--- 
a/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rewrite/token/generator/projection/EncryptProjectionTokenGenerator.java
+++ 
b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rewrite/token/generator/projection/EncryptProjectionTokenGenerator.java
@@ -39,6 +39,7 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.Colu
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ShorthandProjectionSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.OwnerSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.ParenthesesSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
 
 import java.util.Collection;
@@ -98,9 +99,10 @@ public final class EncryptProjectionTokenGenerator {
         Optional<EncryptTable> encryptTable = 
encryptRule.findEncryptTable(columnProjection.getOriginalTable().getValue());
         if (encryptTable.isPresent() && 
encryptTable.get().isEncryptColumn(columnName) && 
!selectStatementContext.containsTableSubquery()) {
             EncryptColumn encryptColumn = 
encryptTable.get().getEncryptColumn(columnName);
-            Collection<Projection> projections = 
generateProjections(encryptColumn, columnProjection, 
selectStatementContext.getSubqueryType(), false);
-            int startIndex = columnSegment.getColumn().getOwner().isPresent() 
? columnSegment.getColumn().getOwner().get().getStopIndex() + 2 : 
columnSegment.getColumn().getStartIndex();
-            int stopIndex = columnSegment.getStopIndex();
+            Collection<Projection> projections = 
generateProjections(encryptColumn, columnProjection, 
selectStatementContext.getSubqueryType());
+            int startIndex = getStartIndex(columnSegment);
+            int stopIndex = getStopIndex(columnSegment);
+            previousSQLTokens.removeIf(each -> each.getStartIndex() == 
startIndex);
             return Optional.of(new SubstitutableColumnNameToken(startIndex, 
stopIndex, projections, databaseType));
         }
         return Optional.empty();
@@ -115,7 +117,7 @@ public final class EncryptProjectionTokenGenerator {
                 Optional<EncryptTable> encryptTable = 
encryptRule.findEncryptTable(columnProjection.getOriginalTable().getValue());
                 if (encryptTable.isPresent() && 
encryptTable.get().isEncryptColumn(columnProjection.getOriginalColumn().getValue())
 && !selectStatementContext.containsTableSubquery()) {
                     EncryptColumn encryptColumn = 
encryptTable.get().getEncryptColumn(columnProjection.getOriginalColumn().getValue());
-                    projections.addAll(generateProjections(encryptColumn, 
columnProjection, subqueryType, true));
+                    projections.addAll(generateProjections(encryptColumn, 
columnProjection, subqueryType));
                     continue;
                 }
             }
@@ -127,69 +129,92 @@ public final class EncryptProjectionTokenGenerator {
         return new SubstitutableColumnNameToken(startIndex, 
segment.getStopIndex(), projections, selectStatementContext.getDatabaseType());
     }
     
+    private int getStartIndex(final ColumnProjectionSegment columnSegment) {
+        if (columnSegment.getColumn().getLeftParentheses().isPresent()) {
+            return 
columnSegment.getColumn().getLeftParentheses().get().getStartIndex();
+        }
+        return columnSegment.getColumn().getOwner().isPresent() ? 
columnSegment.getColumn().getOwner().get().getStartIndex() : 
columnSegment.getColumn().getStartIndex();
+    }
+    
+    private int getStopIndex(final ColumnProjectionSegment columnSegment) {
+        if (columnSegment.getAliasSegment().isPresent()) {
+            return columnSegment.getAliasSegment().get().getStopIndex();
+        }
+        return columnSegment.getColumn().getRightParentheses().isPresent() ? 
columnSegment.getColumn().getRightParentheses().get().getStopIndex() : 
columnSegment.getColumn().getStopIndex();
+    }
+    
     private ColumnProjection buildColumnProjection(final 
ColumnProjectionSegment segment) {
         IdentifierValue owner = 
segment.getColumn().getOwner().map(OwnerSegment::getIdentifier).orElse(null);
-        ColumnProjection result = new ColumnProjection(owner, 
segment.getColumn().getIdentifier(), segment.getAliasName().isPresent() ? 
segment.getAlias().orElse(null) : null, databaseType);
+        ColumnProjection result = new ColumnProjection(owner, 
segment.getColumn().getIdentifier(), segment.getAliasName().isPresent() ? 
segment.getAlias().orElse(null) : null, databaseType,
+                segment.getColumn().getLeftParentheses().orElse(null), 
segment.getColumn().getRightParentheses().orElse(null));
         
result.setOriginalColumn(segment.getColumn().getColumnBoundInfo().getOriginalColumn());
         
result.setOriginalTable(segment.getColumn().getColumnBoundInfo().getOriginalTable());
         return result;
     }
     
     private Collection<Projection> generateProjections(final EncryptColumn 
encryptColumn, final ColumnProjection columnProjection,
-                                                       final SubqueryType 
subqueryType, final boolean shorthandProjection) {
+                                                       final SubqueryType 
subqueryType) {
         if (null == subqueryType || SubqueryType.PROJECTION == subqueryType) {
-            return Collections.singleton(generateProjection(encryptColumn, 
columnProjection, shorthandProjection));
+            return Collections.singleton(generateProjection(encryptColumn, 
columnProjection));
         }
         if (SubqueryType.TABLE == subqueryType || SubqueryType.JOIN == 
subqueryType) {
-            return generateProjectionsInTableSegmentSubquery(encryptColumn, 
columnProjection, shorthandProjection, subqueryType);
+            return generateProjectionsInTableSegmentSubquery(encryptColumn, 
columnProjection, subqueryType);
         }
         if (SubqueryType.PREDICATE == subqueryType) {
-            return 
Collections.singleton(generateProjectionInPredicateSubquery(encryptColumn, 
columnProjection, shorthandProjection));
+            return 
Collections.singleton(generateProjectionInPredicateSubquery(encryptColumn, 
columnProjection));
         }
         if (SubqueryType.INSERT_SELECT == subqueryType) {
-            return generateProjectionsInInsertSelectSubquery(encryptColumn, 
columnProjection, shorthandProjection);
+            return generateProjectionsInInsertSelectSubquery(encryptColumn, 
columnProjection);
         }
         throw new UnsupportedSQLOperationException(
                 "Projections not in simple select, table subquery, join 
subquery, predicate subquery and insert select subquery are not supported in 
encrypt feature.");
     }
     
-    private ColumnProjection generateProjection(final EncryptColumn 
encryptColumn, final ColumnProjection columnProjection, final boolean 
shorthandProjection) {
-        IdentifierValue encryptColumnOwner = shorthandProjection ? 
columnProjection.getOwner().orElse(null) : null;
+    private ColumnProjection generateProjection(final EncryptColumn 
encryptColumn, final ColumnProjection columnProjection) {
         IdentifierValue cipherColumnName = new 
IdentifierValue(encryptColumn.getCipher().getName(), 
columnProjection.getName().getQuoteCharacter());
-        return new ColumnProjection(encryptColumnOwner, cipherColumnName, 
columnProjection.getAlias().orElse(columnProjection.getName()), databaseType);
+        return new ColumnProjection(columnProjection.getOwner().orElse(null), 
cipherColumnName, 
columnProjection.getAlias().orElse(columnProjection.getName()), databaseType,
+                columnProjection.getLeftParentheses().orElse(null), 
columnProjection.getRightParentheses().orElse(null));
     }
     
-    private Collection<Projection> 
generateProjectionsInTableSegmentSubquery(final EncryptColumn encryptColumn, 
final ColumnProjection columnProjection,
-                                                                             
final boolean shorthandProjection, final SubqueryType subqueryType) {
+    private Collection<Projection> 
generateProjectionsInTableSegmentSubquery(final EncryptColumn encryptColumn, 
final ColumnProjection columnProjection, final SubqueryType subqueryType) {
         Collection<Projection> result = new LinkedList<>();
-        IdentifierValue encryptColumnOwner = shorthandProjection ? 
columnProjection.getOwner().orElse(null) : null;
         QuoteCharacter quoteCharacter = 
columnProjection.getName().getQuoteCharacter();
         IdentifierValue cipherColumnName = new 
IdentifierValue(encryptColumn.getCipher().getName(), quoteCharacter);
         IdentifierValue alias = SubqueryType.JOIN == subqueryType ? null : 
columnProjection.getAlias().orElse(columnProjection.getName());
-        result.add(new ColumnProjection(encryptColumnOwner, cipherColumnName, 
alias, databaseType));
+        ParenthesesSegment leftParentheses = 
columnProjection.getLeftParentheses().orElse(null);
+        ParenthesesSegment rightParentheses = 
columnProjection.getRightParentheses().orElse(null);
+        result.add(new 
ColumnProjection(columnProjection.getOwner().orElse(null), cipherColumnName, 
alias, databaseType, leftParentheses, rightParentheses));
         IdentifierValue assistedColumOwner = 
columnProjection.getOwner().orElse(null);
-        encryptColumn.getAssistedQuery().ifPresent(optional -> result.add(new 
ColumnProjection(assistedColumOwner, new IdentifierValue(optional.getName(), 
quoteCharacter), null, databaseType)));
-        encryptColumn.getLikeQuery().ifPresent(optional -> result.add(new 
ColumnProjection(assistedColumOwner, new IdentifierValue(optional.getName(), 
quoteCharacter), null, databaseType)));
+        encryptColumn.getAssistedQuery().ifPresent(
+                optional -> result.add(new 
ColumnProjection(assistedColumOwner, new IdentifierValue(optional.getName(), 
quoteCharacter), null, databaseType, leftParentheses, rightParentheses)));
+        encryptColumn.getLikeQuery().ifPresent(
+                optional -> result.add(new 
ColumnProjection(assistedColumOwner, new IdentifierValue(optional.getName(), 
quoteCharacter), null, databaseType, leftParentheses, rightParentheses)));
         return result;
     }
     
-    private ColumnProjection generateProjectionInPredicateSubquery(final 
EncryptColumn encryptColumn, final ColumnProjection columnProjection, final 
boolean shorthandProjection) {
-        IdentifierValue owner = shorthandProjection ? 
columnProjection.getOwner().orElse(null) : null;
+    private ColumnProjection generateProjectionInPredicateSubquery(final 
EncryptColumn encryptColumn, final ColumnProjection columnProjection) {
         QuoteCharacter quoteCharacter = 
columnProjection.getName().getQuoteCharacter();
-        return encryptColumn.getAssistedQuery().map(optional -> new 
ColumnProjection(owner, new IdentifierValue(optional.getName(), 
quoteCharacter), null, databaseType))
+        ParenthesesSegment leftParentheses = 
columnProjection.getLeftParentheses().orElse(null);
+        ParenthesesSegment rightParentheses = 
columnProjection.getRightParentheses().orElse(null);
+        IdentifierValue owner = columnProjection.getOwner().orElse(null);
+        return encryptColumn.getAssistedQuery()
+                .map(optional -> new ColumnProjection(owner, new 
IdentifierValue(optional.getName(), quoteCharacter), null, databaseType, 
leftParentheses, rightParentheses))
                 .orElseGet(() -> new ColumnProjection(owner, new 
IdentifierValue(encryptColumn.getCipher().getName(), quoteCharacter), 
columnProjection.getAlias().orElse(columnProjection.getName()),
-                        databaseType));
+                        databaseType, leftParentheses, rightParentheses));
     }
     
-    private Collection<Projection> 
generateProjectionsInInsertSelectSubquery(final EncryptColumn encryptColumn, 
final ColumnProjection columnProjection, final boolean shorthandProjection) {
+    private Collection<Projection> 
generateProjectionsInInsertSelectSubquery(final EncryptColumn encryptColumn, 
final ColumnProjection columnProjection) {
         QuoteCharacter quoteCharacter = 
columnProjection.getName().getQuoteCharacter();
         IdentifierValue columnName = new 
IdentifierValue(encryptColumn.getCipher().getName(), quoteCharacter);
         Collection<Projection> result = new LinkedList<>();
-        IdentifierValue encryptColumnOwner = shorthandProjection ? 
columnProjection.getOwner().orElse(null) : null;
-        result.add(new ColumnProjection(encryptColumnOwner, columnName, null, 
databaseType));
+        ParenthesesSegment leftParentheses = 
columnProjection.getLeftParentheses().orElse(null);
+        ParenthesesSegment rightParentheses = 
columnProjection.getRightParentheses().orElse(null);
+        result.add(new 
ColumnProjection(columnProjection.getOwner().orElse(null), columnName, null, 
databaseType, leftParentheses, rightParentheses));
         IdentifierValue assistedColumOwner = 
columnProjection.getOwner().orElse(null);
-        encryptColumn.getAssistedQuery().ifPresent(optional -> result.add(new 
ColumnProjection(assistedColumOwner, new IdentifierValue(optional.getName(), 
quoteCharacter), null, databaseType)));
-        encryptColumn.getLikeQuery().ifPresent(optional -> result.add(new 
ColumnProjection(assistedColumOwner, new IdentifierValue(optional.getName(), 
quoteCharacter), null, databaseType)));
+        encryptColumn.getAssistedQuery().ifPresent(
+                optional -> result.add(new 
ColumnProjection(assistedColumOwner, new IdentifierValue(optional.getName(), 
quoteCharacter), null, databaseType, leftParentheses, rightParentheses)));
+        encryptColumn.getLikeQuery().ifPresent(
+                optional -> result.add(new 
ColumnProjection(assistedColumOwner, new IdentifierValue(optional.getName(), 
quoteCharacter), null, databaseType, leftParentheses, rightParentheses)));
         return result;
     }
     
diff --git 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/projection/engine/ProjectionEngine.java
 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/projection/engine/ProjectionEngine.java
index 41452ad7f64..f6b86183941 100644
--- 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/projection/engine/ProjectionEngine.java
+++ 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/projection/engine/ProjectionEngine.java
@@ -108,7 +108,8 @@ public final class ProjectionEngine {
     private ColumnProjection createProjection(final ColumnProjectionSegment 
projectionSegment) {
         IdentifierValue owner = 
projectionSegment.getColumn().getOwner().isPresent() ? 
projectionSegment.getColumn().getOwner().get().getIdentifier() : null;
         IdentifierValue alias = projectionSegment.getAliasName().isPresent() ? 
projectionSegment.getAlias().orElse(null) : null;
-        ColumnProjection result = new ColumnProjection(owner, 
projectionSegment.getColumn().getIdentifier(), alias, databaseType);
+        ColumnProjection result = new ColumnProjection(owner, 
projectionSegment.getColumn().getIdentifier(), alias, databaseType, 
projectionSegment.getColumn().getLeftParentheses().orElse(null),
+                
projectionSegment.getColumn().getRightParentheses().orElse(null));
         
result.setOriginalColumn(projectionSegment.getColumn().getColumnBoundInfo().getOriginalColumn());
         
result.setOriginalTable(projectionSegment.getColumn().getColumnBoundInfo().getOriginalTable());
         return result;
diff --git 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/projection/impl/ColumnProjection.java
 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/projection/impl/ColumnProjection.java
index 1f46f86b3e7..4813648975a 100644
--- 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/projection/impl/ColumnProjection.java
+++ 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/projection/impl/ColumnProjection.java
@@ -28,6 +28,7 @@ import 
org.apache.shardingsphere.infra.binder.context.segment.select.projection.
 import 
org.apache.shardingsphere.infra.database.core.metadata.database.enums.QuoteCharacter;
 import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
 import org.apache.shardingsphere.infra.database.mysql.type.MySQLDatabaseType;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.ParenthesesSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
 
 import java.util.Optional;
@@ -50,13 +51,21 @@ public final class ColumnProjection implements Projection {
     
     private final DatabaseType databaseType;
     
+    private final ParenthesesSegment leftParentheses;
+    
+    private final ParenthesesSegment rightParentheses;
+    
     private IdentifierValue originalTable;
     
     private IdentifierValue originalColumn;
     
     public ColumnProjection(final String owner, final String name, final 
String alias, final DatabaseType databaseType) {
         this(null == owner ? null : new IdentifierValue(owner, 
QuoteCharacter.NONE), new IdentifierValue(name, QuoteCharacter.NONE),
-                null == alias ? null : new IdentifierValue(alias, 
QuoteCharacter.NONE), databaseType);
+                null == alias ? null : new IdentifierValue(alias, 
QuoteCharacter.NONE), databaseType, null, null);
+    }
+    
+    public ColumnProjection(final IdentifierValue owner, final IdentifierValue 
name, final IdentifierValue alias, final DatabaseType databaseType) {
+        this(owner, name, alias, databaseType, null, null);
     }
     
     @Override
@@ -110,4 +119,22 @@ public final class ColumnProjection implements Projection {
     public IdentifierValue getOriginalColumn() {
         return null == originalColumn || 
Strings.isNullOrEmpty(originalColumn.getValue()) ? name : originalColumn;
     }
+    
+    /**
+     * Get left parentheses.
+     *
+     * @return left parentheses
+     */
+    public Optional<ParenthesesSegment> getLeftParentheses() {
+        return Optional.ofNullable(leftParentheses);
+    }
+    
+    /**
+     * Get right parentheses.
+     *
+     * @return right parentheses
+     */
+    public Optional<ParenthesesSegment> getRightParentheses() {
+        return Optional.ofNullable(rightParentheses);
+    }
 }
diff --git 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/expression/type/ColumnSegmentBinder.java
 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/expression/type/ColumnSegmentBinder.java
index 18fe0ad7cda..527957067bf 100644
--- 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/expression/type/ColumnSegmentBinder.java
+++ 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/expression/type/ColumnSegmentBinder.java
@@ -86,7 +86,8 @@ public final class ColumnSegmentBinder {
     private static ColumnSegment copy(final ColumnSegment segment) {
         ColumnSegment result = new ColumnSegment(segment.getStartIndex(), 
segment.getStopIndex(), segment.getIdentifier());
         segment.getOwner().ifPresent(result::setOwner);
-        result.getParentheses().addAll(segment.getParentheses());
+        segment.getLeftParentheses().ifPresent(result::setLeftParentheses);
+        segment.getRightParentheses().ifPresent(result::setRightParentheses);
         return result;
     }
     
diff --git 
a/infra/rewrite/src/main/java/org/apache/shardingsphere/infra/rewrite/sql/token/pojo/generic/SubstitutableColumnNameToken.java
 
b/infra/rewrite/src/main/java/org/apache/shardingsphere/infra/rewrite/sql/token/pojo/generic/SubstitutableColumnNameToken.java
index 6a60f337de3..6587868b099 100644
--- 
a/infra/rewrite/src/main/java/org/apache/shardingsphere/infra/rewrite/sql/token/pojo/generic/SubstitutableColumnNameToken.java
+++ 
b/infra/rewrite/src/main/java/org/apache/shardingsphere/infra/rewrite/sql/token/pojo/generic/SubstitutableColumnNameToken.java
@@ -57,8 +57,8 @@ public final class SubstitutableColumnNameToken extends 
SQLToken implements Subs
     public SubstitutableColumnNameToken(final int startIndex, final int 
stopIndex, final Collection<Projection> projections, final DatabaseType 
databaseType) {
         super(startIndex);
         this.stopIndex = stopIndex;
-        this.lastColumn = false;
-        this.quoteCharacter = new 
DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getQuoteCharacter();
+        lastColumn = false;
+        quoteCharacter = new 
DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getQuoteCharacter();
         this.projections = projections;
     }
     
@@ -103,12 +103,14 @@ public final class SubstitutableColumnNameToken extends 
SQLToken implements Subs
     }
     
     private void appendColumnProjection(final ColumnProjection 
columnProjection, final Map<String, String> logicActualTableNames, final 
StringBuilder builder) {
+        columnProjection.getLeftParentheses().ifPresent(optional -> 
builder.append("("));
         if (columnProjection.getOwner().isPresent()) {
             IdentifierValue owner = columnProjection.getOwner().get();
             String actualTableOwner = 
logicActualTableNames.getOrDefault(owner.getValue(), owner.getValue());
             builder.append(getValueWithQuoteCharacters(new 
IdentifierValue(actualTableOwner, owner.getQuoteCharacter()))).append('.');
         }
         
builder.append(getValueWithQuoteCharacters(columnProjection.getName()));
+        columnProjection.getRightParentheses().ifPresent(optional -> 
builder.append(")"));
         if (columnProjection.getAlias().isPresent()) {
             builder.append(" AS 
").append(getValueWithQuoteCharacters(columnProjection.getAlias().get()));
         }
diff --git 
a/parser/sql/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/doris/visitor/statement/DorisStatementVisitor.java
 
b/parser/sql/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/doris/visitor/statement/DorisStatementVisitor.java
index bf2b281dc0d..7f9e7fafffb 100644
--- 
a/parser/sql/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/doris/visitor/statement/DorisStatementVisitor.java
+++ 
b/parser/sql/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/doris/visitor/statement/DorisStatementVisitor.java
@@ -643,8 +643,8 @@ public abstract class DorisStatementVisitor extends 
DorisStatementBaseVisitor<AS
         if (null != ctx.LP_() && 1 == ctx.expr().size()) {
             ASTNode result = visit(ctx.expr(0));
             if (result instanceof ColumnSegment) {
-                ((ColumnSegment) result).getParentheses().add(new 
ParenthesesSegment(ctx.LP_().getSymbol().getStartIndex(), 
ctx.LP_().getSymbol().getStopIndex(), true));
-                ((ColumnSegment) result).getParentheses().add(new 
ParenthesesSegment(ctx.RP_().getSymbol().getStartIndex(), 
ctx.RP_().getSymbol().getStopIndex(), false));
+                ((ColumnSegment) result).setLeftParentheses(new 
ParenthesesSegment(ctx.LP_().getSymbol().getStartIndex(), 
ctx.LP_().getSymbol().getStopIndex(), ctx.LP_().getSymbol().getText()));
+                ((ColumnSegment) result).setRightParentheses(new 
ParenthesesSegment(ctx.RP_().getSymbol().getStartIndex(), 
ctx.RP_().getSymbol().getStopIndex(), ctx.RP_().getSymbol().getText()));
             }
             return result;
         }
diff --git 
a/parser/sql/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/MySQLStatementVisitor.java
 
b/parser/sql/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/MySQLStatementVisitor.java
index ced409d5982..648a744b71e 100644
--- 
a/parser/sql/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/MySQLStatementVisitor.java
+++ 
b/parser/sql/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/MySQLStatementVisitor.java
@@ -51,6 +51,7 @@ import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.Convert
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.CteClauseContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.CurrentUserFunctionContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.DataTypeContext;
+import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.DatabaseNameContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.DeleteContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.DuplicateSpecificationContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.EngineRefContext;
@@ -110,7 +111,6 @@ import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.Replace
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.ReplaceSelectClauseContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.ReplaceValuesClauseContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.RowConstructorListContext;
-import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.DatabaseNameContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.SelectContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.SelectSpecificationContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.MySQLStatementParser.SelectWithIntoContext;
@@ -643,8 +643,8 @@ public abstract class MySQLStatementVisitor extends 
MySQLStatementBaseVisitor<AS
         if (null != ctx.LP_() && 1 == ctx.expr().size()) {
             ASTNode result = visit(ctx.expr(0));
             if (result instanceof ColumnSegment) {
-                ((ColumnSegment) result).getParentheses().add(new 
ParenthesesSegment(ctx.LP_().getSymbol().getStartIndex(), 
ctx.LP_().getSymbol().getStopIndex(), true));
-                ((ColumnSegment) result).getParentheses().add(new 
ParenthesesSegment(ctx.RP_().getSymbol().getStartIndex(), 
ctx.RP_().getSymbol().getStopIndex(), false));
+                ((ColumnSegment) result).setLeftParentheses(new 
ParenthesesSegment(ctx.LP_().getSymbol().getStartIndex(), 
ctx.LP_().getSymbol().getStopIndex(), ctx.LP_().getSymbol().getText()));
+                ((ColumnSegment) result).setRightParentheses(new 
ParenthesesSegment(ctx.RP_().getSymbol().getStartIndex(), 
ctx.RP_().getSymbol().getStopIndex(), ctx.RP_().getSymbol().getText()));
             }
             return result;
         }
diff --git 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/column/ColumnSegment.java
 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/column/ColumnSegment.java
index 1b0eb5f7a97..b114279424f 100644
--- 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/column/ColumnSegment.java
+++ 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/column/ColumnSegment.java
@@ -20,13 +20,12 @@ package 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column;
 import lombok.Getter;
 import lombok.Setter;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExpressionSegment;
-import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.ParenthesesSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.OwnerAvailable;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.OwnerSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.ParenthesesSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.bound.ColumnSegmentBoundInfo;
 import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
 
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
@@ -54,7 +53,9 @@ public final class ColumnSegment implements 
ExpressionSegment, OwnerAvailable {
     
     private boolean isVariable;
     
-    private List<ParenthesesSegment> parentheses = new LinkedList<>();
+    private ParenthesesSegment leftParentheses;
+    
+    private ParenthesesSegment rightParentheses;
     
     public ColumnSegment(final int startIndex, final int stopIndex, final 
IdentifierValue identifier) {
         this.startIndex = startIndex;
@@ -99,4 +100,22 @@ public final class ColumnSegment implements 
ExpressionSegment, OwnerAvailable {
     public String getText() {
         return getExpression();
     }
+    
+    /**
+     * Get left parentheses.
+     *
+     * @return left parentheses
+     */
+    public Optional<ParenthesesSegment> getLeftParentheses() {
+        return Optional.ofNullable(leftParentheses);
+    }
+    
+    /**
+     * Get right parentheses.
+     *
+     * @return right parentheses
+     */
+    public Optional<ParenthesesSegment> getRightParentheses() {
+        return Optional.ofNullable(rightParentheses);
+    }
 }
diff --git 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/generic/ParenthesesSegment.java
 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/generic/ParenthesesSegment.java
index ceefc0765f7..927ce2d3cbe 100644
--- 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/generic/ParenthesesSegment.java
+++ 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/generic/ParenthesesSegment.java
@@ -17,6 +17,7 @@
 
 package org.apache.shardingsphere.sql.parser.statement.core.segment.generic;
 
+import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.SQLSegment;
@@ -26,11 +27,12 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.segment.SQLSegment;
  */
 @RequiredArgsConstructor
 @Getter
+@EqualsAndHashCode
 public final class ParenthesesSegment implements SQLSegment {
     
     private final int startIndex;
     
     private final int stopIndex;
     
-    private final boolean left;
+    private final String parentheses;
 }
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/generic/ParenthesesAssert.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/generic/ParenthesesAssert.java
index b3340015011..10ae6147584 100644
--- 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/generic/ParenthesesAssert.java
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/generic/ParenthesesAssert.java
@@ -41,7 +41,7 @@ public final class ParenthesesAssert {
      * @param expected expected parentheses
      */
     public static void assertIs(final SQLCaseAssertContext assertContext, 
final ParenthesesSegment actual, final ExpectedParentheses expected) {
-        assertThat(assertContext.getText("Left brackets assertion error: "), 
actual.isLeft(), is(expected.isLeft()));
+        assertThat(assertContext.getText("Parentheses assertion error: "), 
actual.getParentheses(), is(expected.getParentheses()));
         SQLSegmentAssert.assertIs(assertContext, actual, expected);
     }
 }
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/projection/ProjectionAssert.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/projection/ProjectionAssert.java
index b0df5d78b06..7a8fbe2a079 100644
--- 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/projection/ProjectionAssert.java
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/projection/ProjectionAssert.java
@@ -30,7 +30,6 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.Subq
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.rownum.NumberLiteralRowNumberValueSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.rownum.ParameterMarkerRowNumberValueSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.top.TopProjectionSegment;
-import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.ParenthesesSegment;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.SQLCaseAssertContext;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.SQLSegmentAssert;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.expression.ExpressionAssert;
@@ -49,7 +48,6 @@ import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.s
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.projection.impl.top.ExpectedTopProjection;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.sql.type.SQLCaseType;
 
-import java.util.Iterator;
 import java.util.List;
 
 import static org.hamcrest.CoreMatchers.instanceOf;
@@ -139,11 +137,8 @@ public final class ProjectionAssert {
         } else {
             IdentifierValueAssert.assertIs(assertContext, 
actual.getColumn().getIdentifier(), expected, "Column projection");
         }
-        if (!expected.getParentheses().isEmpty()) {
-            assertThat(expected.getParentheses().size(), 
is(actual.getColumn().getParentheses().size()));
-            Iterator<ParenthesesSegment> iterator = 
actual.getColumn().getParentheses().iterator();
-            expected.getParentheses().forEach(each -> 
ParenthesesAssert.assertIs(assertContext, iterator.next(), each));
-        }
+        assertLeftParentheses(assertContext, actual, expected);
+        assertRightParentheses(assertContext, actual, expected);
         if (null == expected.getOwner()) {
             assertFalse(actual.getColumn().getOwner().isPresent(), 
assertContext.getText("Actual owner should not exist."));
         } else {
@@ -152,6 +147,24 @@ public final class ProjectionAssert {
         }
     }
     
+    private static void assertLeftParentheses(final SQLCaseAssertContext 
assertContext, final ColumnProjectionSegment actual, final 
ExpectedColumnProjection expected) {
+        if (null == expected.getLeftParentheses()) {
+            assertFalse(actual.getColumn().getLeftParentheses().isPresent(), 
assertContext.getText("Actual left parentheses should not exist."));
+        } else {
+            assertTrue(actual.getColumn().getLeftParentheses().isPresent(), 
assertContext.getText("Actual left parentheses should exist."));
+            ParenthesesAssert.assertIs(assertContext, 
actual.getColumn().getLeftParentheses().get(), expected.getLeftParentheses());
+        }
+    }
+    
+    private static void assertRightParentheses(final SQLCaseAssertContext 
assertContext, final ColumnProjectionSegment actual, final 
ExpectedColumnProjection expected) {
+        if (null == expected.getRightParentheses()) {
+            assertFalse(actual.getColumn().getRightParentheses().isPresent(), 
assertContext.getText("Actual right parentheses should not exist."));
+        } else {
+            assertTrue(actual.getColumn().getRightParentheses().isPresent(), 
assertContext.getText("Actual right parentheses should exist."));
+            ParenthesesAssert.assertIs(assertContext, 
actual.getColumn().getRightParentheses().get(), expected.getRightParentheses());
+        }
+    }
+    
     private static void assertAggregationProjection(final SQLCaseAssertContext 
assertContext, final AggregationProjectionSegment actual, final 
ExpectedAggregationProjection expected) {
         assertThat(assertContext.getText("Aggregation projection type 
assertion error: "), actual.getType().name(), is(expected.getType()));
         assertThat(assertContext.getText("Aggregation projection inner 
expression assertion error: "), actual.getExpression(), 
is(expected.getExpression()));
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/generic/ExpectedParentheses.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/generic/ExpectedParentheses.java
index cb3c8f44358..31d671f0a6b 100644
--- 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/generic/ExpectedParentheses.java
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/generic/ExpectedParentheses.java
@@ -30,6 +30,6 @@ import javax.xml.bind.annotation.XmlAttribute;
 @Setter
 public final class ExpectedParentheses extends AbstractExpectedSQLSegment {
     
-    @XmlAttribute(name = "left")
-    private boolean left;
+    @XmlAttribute(name = "parentheses")
+    private String parentheses;
 }
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/projection/impl/column/ExpectedColumnProjection.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/projection/impl/column/ExpectedColumnProjection.java
index ef22e08dd2a..565e140fd47 100644
--- 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/projection/impl/column/ExpectedColumnProjection.java
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/projection/impl/column/ExpectedColumnProjection.java
@@ -26,8 +26,6 @@ import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.s
 
 import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlElement;
-import java.util.LinkedList;
-import java.util.List;
 
 /**
  * Expected column projection.
@@ -42,6 +40,9 @@ public final class ExpectedColumnProjection extends 
AbstractExpectedIdentifierSQ
     @XmlElement
     private ExpectedOwner owner;
     
-    @XmlElement(name = "parentheses")
-    private List<ExpectedParentheses> parentheses = new LinkedList<>();
+    @XmlElement(name = "left-parentheses")
+    private ExpectedParentheses leftParentheses;
+    
+    @XmlElement(name = "right-parentheses")
+    private ExpectedParentheses rightParentheses;
 }
diff --git a/test/it/parser/src/main/resources/case/dml/select.xml 
b/test/it/parser/src/main/resources/case/dml/select.xml
index 355d31ac6d2..ea7418f5938 100644
--- a/test/it/parser/src/main/resources/case/dml/select.xml
+++ b/test/it/parser/src/main/resources/case/dml/select.xml
@@ -2889,8 +2889,8 @@
         </from>
         <projections start-index="16" stop-index="24" distinct-row="true">
             <column-projection name="item_id" start-index="17" stop-index="23">
-                <parentheses left = "true" start-index = "16" stop-index = 
"16" />
-                <parentheses left = "false" start-index = "24" stop-index = 
"24" />
+                <left-parentheses parentheses = "(" start-index = "16" 
stop-index = "16" />
+                <right-parentheses parentheses = ")" start-index = "24" 
stop-index = "24" />
             </column-projection>
         </projections>
     </select>
@@ -3243,7 +3243,10 @@
             <simple-table name="t_order_item" start-index="30" stop-index="41" 
/>
         </from>
         <projections distinct-row="true" start-index="15" stop-index="23">
-            <column-projection start-index="16" stop-index="22" name="item_id" 
/>
+            <column-projection start-index="16" stop-index="22" name="item_id">
+                <left-parentheses parentheses="(" start-index="15" 
stop-index="15" />
+                <right-parentheses parentheses=")" start-index="23" 
stop-index="23" />
+            </column-projection>
         </projections>
         <order-by>
             <column-item name="item_id" start-index="52" stop-index="58" />
diff --git 
a/test/it/parser/src/main/resources/sql/supported/dml/select-distinct.xml 
b/test/it/parser/src/main/resources/sql/supported/dml/select-distinct.xml
index 92c9e835ce0..8901e853b06 100644
--- a/test/it/parser/src/main/resources/sql/supported/dml/select-distinct.xml
+++ b/test/it/parser/src/main/resources/sql/supported/dml/select-distinct.xml
@@ -28,7 +28,6 @@
     <sql-case id="select_distinct_with_owner_star_without_order_by" 
value="SELECT DISTINCT t_order.*, t_order_item.order_id FROM t_order, 
t_order_item WHERE t_order.order_id = t_order_item.order_id" db-types="MySQL" />
     <!-- for with owner column with group by without order by  -->
     <sql-case id="select_distinct_with_owner_column_with_group_by" 
value="SELECT DISTINCT t_order.order_id FROM t_order GROUP BY t_order.order_id" 
db-types="MySQL" />
-
     <sql-case id="select_distinct_with_single_column" value="SELECT DISTINCT 
item_id FROM t_order_item ORDER BY item_id" />
     <sql-case id="select_distinct_with_multi_column" value="SELECT DISTINCT 
order_id, user_id, status FROM t_order ORDER BY order_id" />
     <sql-case id="select_distinct_with_owner_column" value="SELECT DISTINCT 
t_order.order_id FROM t_order ORDER BY order_id" />
@@ -41,7 +40,8 @@
     <sql-case id="select_distinct_with_count_sum" value="SELECT COUNT(DISTINCT 
order_id), SUM(DISTINCT order_id) FROM t_order WHERE order_id &lt; 1100" 
db-types="MySQL" />
     <sql-case id="select_distinct_with_single_count_group_by" value="SELECT 
order_id, COUNT(DISTINCT order_id) c FROM t_order WHERE order_id &lt; 1100 
GROUP BY order_id ORDER BY order_id" />
     <sql-case id="select_distinct_with_count_group_by" value="SELECT 
COUNT(DISTINCT order_id) c, order_id FROM t_order GROUP BY order_id ORDER BY 
order_id" />
-    <sql-case id="select_distinct_function" value="SELECT DISTINCT(item_id) 
FROM t_order_item ORDER BY item_id" 
db-types="H2,MySQL,PostgreSQL,openGauss,Oracle,SQLServer" />
+    <!-- TODO support more database type like 
PostgreSQL,openGauss,Oracle,SQLServer-->
+    <sql-case id="select_distinct_function" value="SELECT DISTINCT(item_id) 
FROM t_order_item ORDER BY item_id" db-types="H2,MySQL,Doris" />
     <sql-case id="select_distinct_function_nulls_last" value="SELECT 
DISTINCT(item_id) FROM t_order_item ORDER BY item_id" 
db-types="PostgreSQL,openGauss,Oracle" />
     <sql-case id="select_distinct_with_count_calculation" value="SELECT 
COUNT(DISTINCT user_id + order_id) c FROM t_order WHERE order_id &lt; 1100" />
     <sql-case id="select_distinct_with_aggregation_functions" value="SELECT 
SUM(DISTINCT order_id),count(DISTINCT order_id),count(order_id)  FROM t_order 
WHERE order_id &lt; 1100" />
diff --git 
a/test/it/rewriter/src/test/resources/scenario/encrypt/case/query-with-cipher/dml/select/select-distinct.xml
 
b/test/it/rewriter/src/test/resources/scenario/encrypt/case/query-with-cipher/dml/select/select-distinct.xml
index 793b33f56a1..f1ec58a176c 100644
--- 
a/test/it/rewriter/src/test/resources/scenario/encrypt/case/query-with-cipher/dml/select/select-distinct.xml
+++ 
b/test/it/rewriter/src/test/resources/scenario/encrypt/case/query-with-cipher/dml/select/select-distinct.xml
@@ -21,4 +21,16 @@
         <input sql="SELECT distinct amount FROM t_account" />
         <output sql="SELECT distinct cipher_amount AS amount FROM t_account" />
     </rewrite-assertion>
+    <rewrite-assertion id="select_distinct_cipher_field_with_parentheses" 
db-types="MySQL">
+        <input sql="SELECT distinct (amount) FROM t_account" />
+        <output sql="SELECT distinct (cipher_amount) AS amount FROM t_account" 
/>
+    </rewrite-assertion>
+    <rewrite-assertion 
id="select_distinct_cipher_field_with_parentheses_with_alias" db-types="MySQL">
+        <input sql="SELECT distinct (amount) as a FROM t_account" />
+        <output sql="SELECT distinct (cipher_amount) AS a FROM t_account" />
+    </rewrite-assertion>
+    <rewrite-assertion 
id="select_distinct_cipher_field_with_parentheses_with_owner" db-types="MySQL">
+        <input sql="SELECT distinct (a.amount) from t_account a" />
+        <output sql="SELECT distinct (a.cipher_amount) AS amount from 
t_account a" />
+    </rewrite-assertion>
 </rewrite-assertions>

Reply via email to