You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by pa...@apache.org on 2023/07/28 09:59:07 UTC

[shardingsphere] branch master updated: Implement shorthand expand when contains join tables (#27543)

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 <du...@apache.org>
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);
+    }
 }