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);
+ }
}