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

panjuan 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 4b32bdaf673 Implement shorthand expand when contains join tables 
(#27543)
4b32bdaf673 is described below

commit 4b32bdaf673a43d929ca76e1568fea85fcc4121d
Author: Zhengqiang Duan <[email protected]>
AuthorDate: Fri Jul 28 17:59:01 2023 +0800

    Implement shorthand expand when contains join tables (#27543)
---
 .../binder/segment/from/TableSegmentBinder.java    |   4 +-
 .../segment/from/impl/JoinTableSegmentBinder.java  | 122 ++++++++++++++++++++-
 .../projection/ProjectionsSegmentBinder.java       |  14 ++-
 .../impl/ShorthandProjectionSegmentBinder.java     |  33 +++++-
 .../statement/dml/SelectStatementBinder.java       |   8 +-
 .../from/impl/JoinTableSegmentBinderTest.java      |  54 +++++++++
 .../impl/ShorthandProjectionSegmentBinderTest.java |  54 ++++++++-
 .../segment/generic/table/JoinTableSegment.java    |  14 +++
 8 files changed, 284 insertions(+), 19 deletions(-)

diff --git 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/from/TableSegmentBinder.java
 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/from/TableSegmentBinder.java
index 87bd7fc221e..d2f857506c4 100644
--- 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/from/TableSegmentBinder.java
+++ 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/from/TableSegmentBinder.java
@@ -47,8 +47,8 @@ public final class TableSegmentBinder {
      * @param tableBinderContexts table binder contexts
      * @return bounded table segment
      */
-    public static TableSegment bind(final TableSegment segment, final 
ShardingSphereMetaData metaData, final String defaultDatabaseName, final 
DatabaseType databaseType,
-                                    final Map<String, 
TableSegmentBinderContext> tableBinderContexts) {
+    public static TableSegment bind(final TableSegment segment, final 
ShardingSphereMetaData metaData, final String defaultDatabaseName,
+                                    final DatabaseType databaseType, final 
Map<String, TableSegmentBinderContext> tableBinderContexts) {
         if (segment instanceof SimpleTableSegment) {
             return SimpleTableSegmentBinder.bind((SimpleTableSegment) segment, 
metaData, defaultDatabaseName, databaseType, tableBinderContexts);
         }
diff --git 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/from/impl/JoinTableSegmentBinder.java
 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/from/impl/JoinTableSegmentBinder.java
index 3016bce0835..aa1a687bb26 100644
--- 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/from/impl/JoinTableSegmentBinder.java
+++ 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/from/impl/JoinTableSegmentBinder.java
@@ -22,9 +22,21 @@ import lombok.NoArgsConstructor;
 import org.apache.shardingsphere.infra.binder.segment.from.TableSegmentBinder;
 import 
org.apache.shardingsphere.infra.binder.segment.from.TableSegmentBinderContext;
 import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
+import org.apache.shardingsphere.infra.database.mysql.MySQLDatabaseType;
 import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
+import 
org.apache.shardingsphere.infra.util.exception.ShardingSpherePreconditions;
+import org.apache.shardingsphere.sql.parser.sql.common.enums.JoinType;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.column.ColumnSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ColumnProjectionSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ProjectionSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.JoinTableSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SimpleTableSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SubqueryTableSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.TableSegment;
 
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.Map;
 
 /**
@@ -43,11 +55,111 @@ public final class JoinTableSegmentBinder {
      * @param tableBinderContexts table binder contexts
      * @return bounded join table segment
      */
-    public static JoinTableSegment bind(final JoinTableSegment segment, final 
ShardingSphereMetaData metaData, final String defaultDatabaseName, final 
DatabaseType databaseType,
-                                        final Map<String, 
TableSegmentBinderContext> tableBinderContexts) {
-        segment.setLeft(TableSegmentBinder.bind(segment.getLeft(), metaData, 
defaultDatabaseName, databaseType, tableBinderContexts));
-        segment.setRight(TableSegmentBinder.bind(segment.getRight(), metaData, 
defaultDatabaseName, databaseType, tableBinderContexts));
+    public static JoinTableSegment bind(final JoinTableSegment segment, final 
ShardingSphereMetaData metaData, final String defaultDatabaseName,
+                                        final DatabaseType databaseType, final 
Map<String, TableSegmentBinderContext> tableBinderContexts) {
+        JoinTableSegment result = new JoinTableSegment();
+        result.setStartIndex(segment.getStartIndex());
+        result.setStopIndex(segment.getStopIndex());
+        segment.getAliasSegment().ifPresent(result::setAlias);
+        result.setLeft(TableSegmentBinder.bind(segment.getLeft(), metaData, 
defaultDatabaseName, databaseType, tableBinderContexts));
+        result.setNatural(segment.isNatural());
+        result.setJoinType(segment.getJoinType());
+        result.setRight(TableSegmentBinder.bind(segment.getRight(), metaData, 
defaultDatabaseName, databaseType, tableBinderContexts));
+        result.setCondition(segment.getCondition());
+        
result.getJoinTableProjectionSegments().addAll(getJoinTableProjectionSegments(segment,
 databaseType, tableBinderContexts));
         // TODO bind condition and using column in join table segment
-        return segment;
+        return result;
+    }
+    
+    private static Collection<ProjectionSegment> 
getJoinTableProjectionSegments(final JoinTableSegment segment, final 
DatabaseType databaseType,
+                                                                               
 final Map<String, TableSegmentBinderContext> tableBinderContexts) {
+        Collection<ProjectionSegment> projectionSegments = 
getProjectionSegments(segment, databaseType, tableBinderContexts);
+        if (segment.getUsing().isEmpty() && !segment.isNatural()) {
+            return projectionSegments;
+        }
+        Collection<ProjectionSegment> result = new LinkedList<>();
+        Map<String, ProjectionSegment> originalUsingColumns = 
segment.getUsing().isEmpty() ? getUsingColumnsByNaturalJoin(segment, 
tableBinderContexts) : getUsingColumns(segment.getUsing());
+        Collection<ProjectionSegment> orderedUsingColumns =
+                databaseType instanceof MySQLDatabaseType ? 
getJoinUsingColumnsByProjectionOrder(projectionSegments, originalUsingColumns) 
: originalUsingColumns.values();
+        result.addAll(orderedUsingColumns);
+        result.addAll(getJoinRemainingColumns(projectionSegments, 
originalUsingColumns));
+        return result;
+    }
+    
+    private static Collection<ProjectionSegment> getProjectionSegments(final 
JoinTableSegment segment, final DatabaseType databaseType,
+                                                                       final 
Map<String, TableSegmentBinderContext> tableBinderContexts) {
+        Collection<ProjectionSegment> result = new LinkedList<>();
+        if (databaseType instanceof MySQLDatabaseType && 
JoinType.RIGHT.name().equalsIgnoreCase(segment.getJoinType()) && 
(!segment.getUsing().isEmpty() || segment.isNatural())) {
+            result.addAll(getProjectionSegments(segment.getRight(), 
tableBinderContexts));
+            result.addAll(getProjectionSegments(segment.getLeft(), 
tableBinderContexts));
+        } else {
+            result.addAll(getProjectionSegments(segment.getLeft(), 
tableBinderContexts));
+            result.addAll(getProjectionSegments(segment.getRight(), 
tableBinderContexts));
+        }
+        return result;
+    }
+    
+    private static Collection<ProjectionSegment> getProjectionSegments(final 
TableSegment tableSegment, final Map<String, TableSegmentBinderContext> 
tableBinderContexts) {
+        Collection<ProjectionSegment> result = new LinkedList<>();
+        if (tableSegment instanceof SimpleTableSegment) {
+            String tableAliasOrName = tableSegment.getAliasName().orElseGet(() 
-> ((SimpleTableSegment) 
tableSegment).getTableName().getIdentifier().getValue());
+            
result.addAll(getProjectionSegmentsByTableAliasOrName(tableBinderContexts, 
tableAliasOrName));
+        } else if (tableSegment instanceof JoinTableSegment) {
+            result.addAll(((JoinTableSegment) 
tableSegment).getJoinTableProjectionSegments());
+        } else if (tableSegment instanceof SubqueryTableSegment) {
+            
result.addAll(getProjectionSegmentsByTableAliasOrName(tableBinderContexts, 
tableSegment.getAliasName().orElse("")));
+        }
+        return result;
+    }
+    
+    private static Collection<ProjectionSegment> 
getProjectionSegmentsByTableAliasOrName(final Map<String, 
TableSegmentBinderContext> tableBinderContexts, final String tableAliasOrName) {
+        
ShardingSpherePreconditions.checkState(tableBinderContexts.containsKey(tableAliasOrName),
+                () -> new IllegalStateException(String.format("Can not find 
table binder context by table alias or name %s.", tableAliasOrName)));
+        return 
tableBinderContexts.get(tableAliasOrName).getProjectionSegments();
+    }
+    
+    private static Map<String, ProjectionSegment> 
getUsingColumnsByNaturalJoin(final JoinTableSegment segment, final Map<String, 
TableSegmentBinderContext> tableBinderContexts) {
+        Map<String, ProjectionSegment> result = new LinkedHashMap<>();
+        Collection<ProjectionSegment> leftProjections = 
getProjectionSegments(segment.getLeft(), tableBinderContexts);
+        Map<String, ProjectionSegment> rightProjections = new 
LinkedHashMap<>();
+        getProjectionSegments(segment.getRight(), 
tableBinderContexts).forEach(each -> 
rightProjections.put(each.getColumnLabel().toLowerCase(), each));
+        for (ProjectionSegment each : leftProjections) {
+            String columnLabel = each.getColumnLabel().toLowerCase();
+            if (rightProjections.containsKey(columnLabel)) {
+                result.put(columnLabel, each);
+            }
+        }
+        return result;
+    }
+    
+    private static Map<String, ProjectionSegment> getUsingColumns(final 
Collection<ColumnSegment> usingColumns) {
+        Map<String, ProjectionSegment> result = new LinkedHashMap<>();
+        for (ColumnSegment each : usingColumns) {
+            ColumnProjectionSegment columnProjectionSegment = new 
ColumnProjectionSegment(each);
+            result.put(columnProjectionSegment.getColumnLabel().toLowerCase(), 
columnProjectionSegment);
+        }
+        return result;
+    }
+    
+    private static Collection<ProjectionSegment> 
getJoinUsingColumnsByProjectionOrder(final Collection<ProjectionSegment> 
projectionSegments,
+                                                                               
       final Map<String, ProjectionSegment> usingColumns) {
+        Map<String, ProjectionSegment> result = new 
LinkedHashMap<>(usingColumns.size(), 1F);
+        for (ProjectionSegment each : projectionSegments) {
+            String columnLabel = each.getColumnLabel().toLowerCase();
+            if (!result.containsKey(columnLabel) && 
usingColumns.containsKey(columnLabel)) {
+                result.put(columnLabel, each);
+            }
+        }
+        return result.values();
+    }
+    
+    private static Collection<ProjectionSegment> getJoinRemainingColumns(final 
Collection<ProjectionSegment> projectionSegments, final Map<String, 
ProjectionSegment> usingColumns) {
+        Collection<ProjectionSegment> result = new LinkedList<>();
+        for (ProjectionSegment each : projectionSegments) {
+            if 
(!usingColumns.containsKey(each.getColumnLabel().toLowerCase())) {
+                result.add(each);
+            }
+        }
+        return result;
     }
 }
diff --git 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/projection/ProjectionsSegmentBinder.java
 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/projection/ProjectionsSegmentBinder.java
index 8b3ef7f81c3..4aa354feafb 100644
--- 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/projection/ProjectionsSegmentBinder.java
+++ 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/projection/ProjectionsSegmentBinder.java
@@ -22,10 +22,12 @@ import lombok.NoArgsConstructor;
 import 
org.apache.shardingsphere.infra.binder.segment.from.TableSegmentBinderContext;
 import 
org.apache.shardingsphere.infra.binder.segment.projection.impl.ColumnProjectionSegmentBinder;
 import 
org.apache.shardingsphere.infra.binder.segment.projection.impl.ShorthandProjectionSegmentBinder;
+import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ColumnProjectionSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ProjectionSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ProjectionsSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ShorthandProjectionSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.TableSegment;
 
 import java.util.Map;
 
@@ -39,22 +41,26 @@ public final class ProjectionsSegmentBinder {
      * Bind projections segment with metadata.
      *
      * @param segment table segment
+     * @param boundedTableSegment bounded table segment
+     * @param databaseType database type
      * @param tableBinderContexts table binder contexts
      * @return bounded projections segment
      */
-    public static ProjectionsSegment bind(final ProjectionsSegment segment, 
final Map<String, TableSegmentBinderContext> tableBinderContexts) {
+    public static ProjectionsSegment bind(final ProjectionsSegment segment, 
final TableSegment boundedTableSegment,
+                                          final DatabaseType databaseType, 
final Map<String, TableSegmentBinderContext> tableBinderContexts) {
         ProjectionsSegment result = new 
ProjectionsSegment(segment.getStartIndex(), segment.getStopIndex());
         result.setDistinctRow(segment.isDistinctRow());
-        segment.getProjections().forEach(each -> 
result.getProjections().add(bind(each, tableBinderContexts)));
+        segment.getProjections().forEach(each -> 
result.getProjections().add(bind(each, boundedTableSegment, databaseType, 
tableBinderContexts)));
         return result;
     }
     
-    private static ProjectionSegment bind(final ProjectionSegment 
projectionSegment, final Map<String, TableSegmentBinderContext> 
tableBinderContexts) {
+    private static ProjectionSegment bind(final ProjectionSegment 
projectionSegment, final TableSegment boundedTableSegment,
+                                          final DatabaseType databaseType, 
final Map<String, TableSegmentBinderContext> tableBinderContexts) {
         if (projectionSegment instanceof ColumnProjectionSegment) {
             return 
ColumnProjectionSegmentBinder.bind((ColumnProjectionSegment) projectionSegment, 
tableBinderContexts);
         }
         if (projectionSegment instanceof ShorthandProjectionSegment) {
-            return 
ShorthandProjectionSegmentBinder.bind((ShorthandProjectionSegment) 
projectionSegment, tableBinderContexts);
+            return 
ShorthandProjectionSegmentBinder.bind((ShorthandProjectionSegment) 
projectionSegment, boundedTableSegment, tableBinderContexts);
         }
         // TODO support more ProjectionSegment bind
         return projectionSegment;
diff --git 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/projection/impl/ShorthandProjectionSegmentBinder.java
 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/projection/impl/ShorthandProjectionSegmentBinder.java
index 0e8b500eae7..d6b9a19f834 100644
--- 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/projection/impl/ShorthandProjectionSegmentBinder.java
+++ 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/segment/projection/impl/ShorthandProjectionSegmentBinder.java
@@ -20,8 +20,13 @@ package 
org.apache.shardingsphere.infra.binder.segment.projection.impl;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import 
org.apache.shardingsphere.infra.binder.segment.from.TableSegmentBinderContext;
+import 
org.apache.shardingsphere.infra.util.exception.ShardingSpherePreconditions;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ProjectionSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ShorthandProjectionSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.JoinTableSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SimpleTableSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SubqueryTableSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.TableSegment;
 
 import java.util.Collection;
 import java.util.Map;
@@ -36,20 +41,26 @@ public final class ShorthandProjectionSegmentBinder {
      * Bind column projection segment with metadata.
      *
      * @param segment table segment
+     * @param boundedTableSegment bounded table segment
      * @param tableBinderContexts table binder contexts
      * @return bounded column projection segment
      */
-    public static ShorthandProjectionSegment bind(final 
ShorthandProjectionSegment segment, final Map<String, 
TableSegmentBinderContext> tableBinderContexts) {
+    public static ShorthandProjectionSegment bind(final 
ShorthandProjectionSegment segment, final TableSegment boundedTableSegment,
+                                                  final Map<String, 
TableSegmentBinderContext> tableBinderContexts) {
         if (segment.getOwner().isPresent()) {
-            TableSegmentBinderContext tableBinderContext = 
tableBinderContexts.get(segment.getOwner().get().getIdentifier().getValue());
-            expandVisibleColumn(tableBinderContext.getProjectionSegments(), 
segment);
+            
expandVisibleColumn(getProjectionSegmentsByTableAliasOrName(tableBinderContexts,
 segment.getOwner().get().getIdentifier().getValue()), segment);
         } else {
-            // TODO expand according to different database with multi tables
-            tableBinderContexts.values().forEach(each -> 
expandVisibleColumn(each.getProjectionSegments(), segment));
+            bindNoOwnerProjections(segment, boundedTableSegment, 
tableBinderContexts);
         }
         return segment;
     }
     
+    private static Collection<ProjectionSegment> 
getProjectionSegmentsByTableAliasOrName(final Map<String, 
TableSegmentBinderContext> tableBinderContexts, final String tableAliasOrName) {
+        
ShardingSpherePreconditions.checkState(tableBinderContexts.containsKey(tableAliasOrName),
+                () -> new IllegalStateException(String.format("Can not find 
table binder context by table alias or name %s.", tableAliasOrName)));
+        return 
tableBinderContexts.get(tableAliasOrName).getProjectionSegments();
+    }
+    
     private static void expandVisibleColumn(final 
Collection<ProjectionSegment> projectionSegments, final 
ShorthandProjectionSegment shorthandProjectionSegment) {
         for (ProjectionSegment each : projectionSegments) {
             if (each.isVisible()) {
@@ -57,4 +68,16 @@ public final class ShorthandProjectionSegmentBinder {
             }
         }
     }
+    
+    private static void bindNoOwnerProjections(final 
ShorthandProjectionSegment segment, final TableSegment boundedTableSegment,
+                                               final Map<String, 
TableSegmentBinderContext> tableBinderContexts) {
+        if (boundedTableSegment instanceof SimpleTableSegment) {
+            String tableAliasOrName = 
boundedTableSegment.getAliasName().orElseGet(() -> ((SimpleTableSegment) 
boundedTableSegment).getTableName().getIdentifier().getValue());
+            
expandVisibleColumn(getProjectionSegmentsByTableAliasOrName(tableBinderContexts,
 tableAliasOrName), segment);
+        } else if (boundedTableSegment instanceof JoinTableSegment) {
+            expandVisibleColumn(((JoinTableSegment) 
boundedTableSegment).getJoinTableProjectionSegments(), segment);
+        } else if (boundedTableSegment instanceof SubqueryTableSegment) {
+            
expandVisibleColumn(getProjectionSegmentsByTableAliasOrName(tableBinderContexts,
 boundedTableSegment.getAliasName().orElse("")), segment);
+        }
+    }
 }
diff --git 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/statement/dml/SelectStatementBinder.java
 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/statement/dml/SelectStatementBinder.java
index 56062800549..785db2b6638 100644
--- 
a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/statement/dml/SelectStatementBinder.java
+++ 
b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/statement/dml/SelectStatementBinder.java
@@ -23,7 +23,9 @@ import 
org.apache.shardingsphere.infra.binder.segment.from.TableSegmentBinder;
 import 
org.apache.shardingsphere.infra.binder.segment.from.TableSegmentBinderContext;
 import 
org.apache.shardingsphere.infra.binder.segment.projection.ProjectionsSegmentBinder;
 import org.apache.shardingsphere.infra.binder.statement.SQLStatementBinder;
+import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
 import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.TableSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.statement.dml.SelectStatement;
 
 import java.util.Map;
@@ -36,9 +38,11 @@ public final class SelectStatementBinder implements 
SQLStatementBinder<SelectSta
     @Override
     public SelectStatement bind(final SelectStatement sqlStatement, final 
ShardingSphereMetaData metaData, final String defaultDatabaseName) {
         Map<String, TableSegmentBinderContext> tableBinderContexts = new 
CaseInsensitiveMap<>();
-        sqlStatement.setFrom(TableSegmentBinder.bind(sqlStatement.getFrom(), 
metaData, defaultDatabaseName, sqlStatement.getDatabaseType(), 
tableBinderContexts));
+        DatabaseType databaseType = sqlStatement.getDatabaseType();
+        TableSegment boundedTableSegment = 
TableSegmentBinder.bind(sqlStatement.getFrom(), metaData, defaultDatabaseName, 
databaseType, tableBinderContexts);
+        sqlStatement.setFrom(boundedTableSegment);
         sqlStatement.getCombine().ifPresent(optional -> 
sqlStatement.setCombine(CombineSegmentBinder.bind(optional, metaData, 
defaultDatabaseName)));
-        
sqlStatement.setProjections(ProjectionsSegmentBinder.bind(sqlStatement.getProjections(),
 tableBinderContexts));
+        
sqlStatement.setProjections(ProjectionsSegmentBinder.bind(sqlStatement.getProjections(),
 boundedTableSegment, databaseType, tableBinderContexts));
         // TODO support other segment bind in select statement
         return sqlStatement;
     }
diff --git 
a/infra/binder/src/test/java/org/apache/shardingsphere/infra/binder/segment/from/impl/JoinTableSegmentBinderTest.java
 
b/infra/binder/src/test/java/org/apache/shardingsphere/infra/binder/segment/from/impl/JoinTableSegmentBinderTest.java
index 169b78a9044..d69f240b41f 100644
--- 
a/infra/binder/src/test/java/org/apache/shardingsphere/infra/binder/segment/from/impl/JoinTableSegmentBinderTest.java
+++ 
b/infra/binder/src/test/java/org/apache/shardingsphere/infra/binder/segment/from/impl/JoinTableSegmentBinderTest.java
@@ -24,6 +24,8 @@ import 
org.apache.shardingsphere.infra.database.mysql.MySQLDatabaseType;
 import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
 import 
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereColumn;
 import 
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
+import org.apache.shardingsphere.sql.parser.sql.common.enums.JoinType;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.column.ColumnSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.AliasSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.JoinTableSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SimpleTableSegment;
@@ -62,6 +64,7 @@ class JoinTableSegmentBinderTest {
         assertTrue(actual.getRight() instanceof SimpleTableSegment);
         assertThat(((SimpleTableSegment) 
actual.getRight()).getTableName().getOriginalDatabase().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
         assertThat(((SimpleTableSegment) 
actual.getRight()).getTableName().getOriginalSchema().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
+        assertThat(actual.getJoinTableProjectionSegments().size(), is(7));
         assertTrue(tableBinderContexts.containsKey("o"));
         assertTrue(tableBinderContexts.containsKey("i"));
     }
@@ -82,10 +85,61 @@ class JoinTableSegmentBinderTest {
         assertTrue(actual.getRight() instanceof SimpleTableSegment);
         assertThat(((SimpleTableSegment) 
actual.getRight()).getTableName().getOriginalDatabase().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
         assertThat(((SimpleTableSegment) 
actual.getRight()).getTableName().getOriginalSchema().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
+        assertThat(actual.getJoinTableProjectionSegments().size(), is(7));
         assertTrue(tableBinderContexts.containsKey("t_order"));
         assertTrue(tableBinderContexts.containsKey("t_order_item"));
     }
     
+    @Test
+    void assertBindWithNaturalJoin() {
+        JoinTableSegment joinTableSegment = mock(JoinTableSegment.class);
+        SimpleTableSegment leftTable = new SimpleTableSegment(new 
TableNameSegment(0, 0, new IdentifierValue("t_order")));
+        leftTable.setAlias(new AliasSegment(0, 0, new IdentifierValue("o")));
+        SimpleTableSegment rightTable = new SimpleTableSegment(new 
TableNameSegment(0, 0, new IdentifierValue("t_order_item")));
+        rightTable.setAlias(new AliasSegment(0, 0, new IdentifierValue("i")));
+        when(joinTableSegment.getLeft()).thenReturn(leftTable);
+        when(joinTableSegment.getRight()).thenReturn(rightTable);
+        when(joinTableSegment.isNatural()).thenReturn(true);
+        when(joinTableSegment.getJoinType()).thenReturn(JoinType.RIGHT.name());
+        ShardingSphereMetaData metaData = createMetaData();
+        Map<String, TableSegmentBinderContext> tableBinderContexts = new 
CaseInsensitiveMap<>();
+        JoinTableSegment actual = 
JoinTableSegmentBinder.bind(joinTableSegment, metaData, 
DefaultDatabase.LOGIC_NAME, new MySQLDatabaseType(), tableBinderContexts);
+        assertTrue(actual.getLeft() instanceof SimpleTableSegment);
+        assertThat(((SimpleTableSegment) 
actual.getLeft()).getTableName().getOriginalDatabase().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
+        assertThat(((SimpleTableSegment) 
actual.getLeft()).getTableName().getOriginalSchema().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
+        assertTrue(actual.getRight() instanceof SimpleTableSegment);
+        assertThat(((SimpleTableSegment) 
actual.getRight()).getTableName().getOriginalDatabase().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
+        assertThat(((SimpleTableSegment) 
actual.getRight()).getTableName().getOriginalSchema().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
+        assertThat(actual.getJoinTableProjectionSegments().size(), is(4));
+        assertTrue(tableBinderContexts.containsKey("o"));
+        assertTrue(tableBinderContexts.containsKey("i"));
+    }
+    
+    @Test
+    void assertBindWithJoinUsing() {
+        JoinTableSegment joinTableSegment = mock(JoinTableSegment.class);
+        SimpleTableSegment leftTable = new SimpleTableSegment(new 
TableNameSegment(0, 0, new IdentifierValue("t_order")));
+        leftTable.setAlias(new AliasSegment(0, 0, new IdentifierValue("o")));
+        SimpleTableSegment rightTable = new SimpleTableSegment(new 
TableNameSegment(0, 0, new IdentifierValue("t_order_item")));
+        rightTable.setAlias(new AliasSegment(0, 0, new IdentifierValue("i")));
+        when(joinTableSegment.getLeft()).thenReturn(leftTable);
+        when(joinTableSegment.getRight()).thenReturn(rightTable);
+        when(joinTableSegment.getJoinType()).thenReturn(JoinType.RIGHT.name());
+        when(joinTableSegment.getUsing()).thenReturn(Arrays.asList(new 
ColumnSegment(0, 0, new IdentifierValue("status")), new ColumnSegment(0, 0, new 
IdentifierValue("order_id"))));
+        ShardingSphereMetaData metaData = createMetaData();
+        Map<String, TableSegmentBinderContext> tableBinderContexts = new 
CaseInsensitiveMap<>();
+        JoinTableSegment actual = 
JoinTableSegmentBinder.bind(joinTableSegment, metaData, 
DefaultDatabase.LOGIC_NAME, new MySQLDatabaseType(), tableBinderContexts);
+        assertTrue(actual.getLeft() instanceof SimpleTableSegment);
+        assertThat(((SimpleTableSegment) 
actual.getLeft()).getTableName().getOriginalDatabase().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
+        assertThat(((SimpleTableSegment) 
actual.getLeft()).getTableName().getOriginalSchema().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
+        assertTrue(actual.getRight() instanceof SimpleTableSegment);
+        assertThat(((SimpleTableSegment) 
actual.getRight()).getTableName().getOriginalDatabase().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
+        assertThat(((SimpleTableSegment) 
actual.getRight()).getTableName().getOriginalSchema().getValue(), 
is(DefaultDatabase.LOGIC_NAME));
+        assertThat(actual.getJoinTableProjectionSegments().size(), is(5));
+        assertTrue(tableBinderContexts.containsKey("o"));
+        assertTrue(tableBinderContexts.containsKey("i"));
+    }
+    
     private ShardingSphereMetaData createMetaData() {
         ShardingSphereSchema schema = mock(ShardingSphereSchema.class, 
RETURNS_DEEP_STUBS);
         
when(schema.getTable("t_order").getColumnValues()).thenReturn(Arrays.asList(
diff --git 
a/infra/binder/src/test/java/org/apache/shardingsphere/infra/binder/segment/projection/impl/ShorthandProjectionSegmentBinderTest.java
 
b/infra/binder/src/test/java/org/apache/shardingsphere/infra/binder/segment/projection/impl/ShorthandProjectionSegmentBinderTest.java
index 811c84eccd9..ece3353aab6 100644
--- 
a/infra/binder/src/test/java/org/apache/shardingsphere/infra/binder/segment/projection/impl/ShorthandProjectionSegmentBinderTest.java
+++ 
b/infra/binder/src/test/java/org/apache/shardingsphere/infra/binder/segment/projection/impl/ShorthandProjectionSegmentBinderTest.java
@@ -20,19 +20,29 @@ package 
org.apache.shardingsphere.infra.binder.segment.projection.impl;
 import org.apache.commons.collections4.map.CaseInsensitiveMap;
 import 
org.apache.shardingsphere.infra.binder.segment.from.TableSegmentBinderContext;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.column.ColumnSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.subquery.SubquerySegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ColumnProjectionSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ProjectionSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ShorthandProjectionSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.AliasSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.OwnerSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.JoinTableSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SimpleTableSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SubqueryTableSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.TableNameSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.TableSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.value.identifier.IdentifierValue;
+import 
org.apache.shardingsphere.sql.parser.sql.dialect.statement.mysql.dml.MySQLSelectStatement;
 import org.junit.jupiter.api.Test;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Map;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
 
 class ShorthandProjectionSegmentBinderTest {
     
@@ -44,7 +54,49 @@ class ShorthandProjectionSegmentBinderTest {
         ColumnProjectionSegment invisibleColumn = new 
ColumnProjectionSegment(new ColumnSegment(0, 0, new IdentifierValue("status")));
         invisibleColumn.setVisible(false);
         tableBinderContexts.put("o", new 
TableSegmentBinderContext(Arrays.asList(new ColumnProjectionSegment(new 
ColumnSegment(0, 0, new IdentifierValue("order_id"))), invisibleColumn)));
-        ShorthandProjectionSegment actual = 
ShorthandProjectionSegmentBinder.bind(shorthandProjectionSegment, 
tableBinderContexts);
+        ShorthandProjectionSegment actual = 
ShorthandProjectionSegmentBinder.bind(shorthandProjectionSegment, 
mock(TableSegment.class), tableBinderContexts);
+        assertThat(actual.getActualProjectionSegments().size(), is(1));
+        ProjectionSegment visibleColumn = 
actual.getActualProjectionSegments().iterator().next();
+        assertThat(visibleColumn.getColumnLabel(), is("order_id"));
+        assertTrue(visibleColumn.isVisible());
+    }
+    
+    @Test
+    void assertBindWithoutOwnerForSimpleTableSegment() {
+        Map<String, TableSegmentBinderContext> tableBinderContexts = new 
CaseInsensitiveMap<>();
+        ColumnProjectionSegment invisibleColumn = new 
ColumnProjectionSegment(new ColumnSegment(0, 0, new IdentifierValue("status")));
+        invisibleColumn.setVisible(false);
+        tableBinderContexts.put("o", new 
TableSegmentBinderContext(Arrays.asList(new ColumnProjectionSegment(new 
ColumnSegment(0, 0, new IdentifierValue("order_id"))), invisibleColumn)));
+        SimpleTableSegment boundedTableSegment = new SimpleTableSegment(new 
TableNameSegment(0, 0, new IdentifierValue("t_order")));
+        boundedTableSegment.setAlias(new AliasSegment(0, 0, new 
IdentifierValue("o")));
+        ShorthandProjectionSegment actual = 
ShorthandProjectionSegmentBinder.bind(new ShorthandProjectionSegment(0, 0), 
boundedTableSegment, tableBinderContexts);
+        assertThat(actual.getActualProjectionSegments().size(), is(1));
+        ProjectionSegment visibleColumn = 
actual.getActualProjectionSegments().iterator().next();
+        assertThat(visibleColumn.getColumnLabel(), is("order_id"));
+        assertTrue(visibleColumn.isVisible());
+    }
+    
+    @Test
+    void assertBindWithoutOwnerForSubqueryTableSegment() {
+        Map<String, TableSegmentBinderContext> tableBinderContexts = new 
CaseInsensitiveMap<>();
+        ColumnProjectionSegment invisibleColumn = new 
ColumnProjectionSegment(new ColumnSegment(0, 0, new IdentifierValue("status")));
+        invisibleColumn.setVisible(false);
+        tableBinderContexts.put("o", new 
TableSegmentBinderContext(Arrays.asList(new ColumnProjectionSegment(new 
ColumnSegment(0, 0, new IdentifierValue("order_id"))), invisibleColumn)));
+        SubqueryTableSegment boundedTableSegment = new 
SubqueryTableSegment(new SubquerySegment(0, 0, 
mock(MySQLSelectStatement.class)));
+        boundedTableSegment.setAlias(new AliasSegment(0, 0, new 
IdentifierValue("o")));
+        ShorthandProjectionSegment actual = 
ShorthandProjectionSegmentBinder.bind(new ShorthandProjectionSegment(0, 0), 
boundedTableSegment, tableBinderContexts);
+        assertThat(actual.getActualProjectionSegments().size(), is(1));
+        ProjectionSegment visibleColumn = 
actual.getActualProjectionSegments().iterator().next();
+        assertThat(visibleColumn.getColumnLabel(), is("order_id"));
+        assertTrue(visibleColumn.isVisible());
+    }
+    
+    @Test
+    void assertBindWithoutOwnerForJoinTableSegment() {
+        ShorthandProjectionSegment shorthandProjectionSegment = new 
ShorthandProjectionSegment(0, 0);
+        JoinTableSegment boundedTableSegment = new JoinTableSegment();
+        boundedTableSegment.getJoinTableProjectionSegments().add(new 
ColumnProjectionSegment(new ColumnSegment(0, 0, new 
IdentifierValue("order_id"))));
+        ShorthandProjectionSegment actual = 
ShorthandProjectionSegmentBinder.bind(shorthandProjectionSegment, 
boundedTableSegment, Collections.emptyMap());
         assertThat(actual.getActualProjectionSegments().size(), is(1));
         ProjectionSegment visibleColumn = 
actual.getActualProjectionSegments().iterator().next();
         assertThat(visibleColumn.getColumnLabel(), is("order_id"));
diff --git 
a/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/generic/table/JoinTableSegment.java
 
b/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/generic/table/JoinTableSegment.java
index 5956268f5a2..2e5aa535e75 100644
--- 
a/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/generic/table/JoinTableSegment.java
+++ 
b/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/generic/table/JoinTableSegment.java
@@ -21,10 +21,13 @@ import lombok.Getter;
 import lombok.Setter;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.column.ColumnSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExpressionSegment;
+import 
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ProjectionSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.AliasSegment;
 import 
org.apache.shardingsphere.sql.parser.sql.common.value.identifier.IdentifierValue;
 
+import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Optional;
 
@@ -50,6 +53,8 @@ public final class JoinTableSegment implements TableSegment {
     
     private List<ColumnSegment> using = Collections.emptyList();
     
+    private Collection<ProjectionSegment> joinTableProjectionSegments = new 
LinkedList<>();
+    
     @Override
     public Optional<String> getAliasName() {
         return null == alias ? Optional.empty() : 
Optional.ofNullable(alias.getIdentifier().getValue());
@@ -59,4 +64,13 @@ public final class JoinTableSegment implements TableSegment {
     public Optional<IdentifierValue> getAlias() {
         return Optional.ofNullable(alias).map(AliasSegment::getIdentifier);
     }
+    
+    /**
+     * Get alias segment.
+     * 
+     * @return alias segment
+     */
+    public Optional<AliasSegment> getAliasSegment() {
+        return Optional.ofNullable(alias);
+    }
 }


Reply via email to