1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shardingsphere.infra.binder.segment.from.impl;
19
20 import com.google.common.base.Strings;
21 import lombok.AccessLevel;
22 import lombok.NoArgsConstructor;
23 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.util.ProjectionUtils;
24 import org.apache.shardingsphere.infra.binder.segment.from.SimpleTableSegmentBinderContext;
25 import org.apache.shardingsphere.infra.binder.segment.from.TableSegmentBinderContext;
26 import org.apache.shardingsphere.infra.binder.segment.parameter.impl.ParameterMarkerExpressionSegmentBinder;
27 import org.apache.shardingsphere.infra.binder.statement.SQLStatementBinderContext;
28 import org.apache.shardingsphere.infra.binder.statement.dml.SelectStatementBinder;
29 import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
30 import org.apache.shardingsphere.infra.database.core.type.DatabaseTypeRegistry;
31 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.column.ColumnSegment;
32 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExpressionSegment;
33 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.simple.ParameterMarkerExpressionSegment;
34 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.subquery.SubquerySegment;
35 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.AggregationProjectionSegment;
36 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ColumnProjectionSegment;
37 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ExpressionProjectionSegment;
38 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ProjectionSegment;
39 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ShorthandProjectionSegment;
40 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.AliasAvailable;
41 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.AliasSegment;
42 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.OwnerSegment;
43 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.bounded.ColumnSegmentBoundedInfo;
44 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SubqueryTableSegment;
45 import org.apache.shardingsphere.sql.parser.sql.common.statement.dml.SelectStatement;
46 import org.apache.shardingsphere.sql.parser.sql.common.value.identifier.IdentifierValue;
47
48 import java.util.Collection;
49 import java.util.Collections;
50 import java.util.LinkedList;
51 import java.util.Map;
52
53
54
55
56 @NoArgsConstructor(access = AccessLevel.PRIVATE)
57 public final class SubqueryTableSegmentBinder {
58
59
60
61
62
63
64
65
66
67
68 public static SubqueryTableSegment bind(final SubqueryTableSegment segment, final SQLStatementBinderContext statementBinderContext,
69 final Map<String, TableSegmentBinderContext> tableBinderContexts, final Map<String, TableSegmentBinderContext> outerTableBinderContexts) {
70 fillPivotColumnNamesInBinderContext(segment, statementBinderContext);
71 SelectStatement boundedSelect = new SelectStatementBinder().bindCorrelateSubquery(segment.getSubquery().getSelect(), statementBinderContext.getMetaData(),
72 statementBinderContext.getDefaultDatabaseName(), outerTableBinderContexts, statementBinderContext.getExternalTableBinderContexts());
73 SubquerySegment boundedSubquerySegment = new SubquerySegment(segment.getSubquery().getStartIndex(), segment.getSubquery().getStopIndex(), boundedSelect, segment.getSubquery().getText());
74 boundedSubquerySegment.setSubqueryType(segment.getSubquery().getSubqueryType());
75 IdentifierValue subqueryTableName = segment.getAliasSegment().map(AliasSegment::getIdentifier).orElseGet(() -> new IdentifierValue(""));
76 bindParameterMarkerProjection(boundedSubquerySegment, subqueryTableName);
77 SubqueryTableSegment result = new SubqueryTableSegment(segment.getStartIndex(), segment.getStopIndex(), boundedSubquerySegment);
78 segment.getAliasSegment().ifPresent(result::setAlias);
79 tableBinderContexts.put(subqueryTableName.getValue().toLowerCase(),
80 new SimpleTableSegmentBinderContext(createSubqueryProjections(boundedSelect.getProjections().getProjections(), subqueryTableName, statementBinderContext.getDatabaseType())));
81 return result;
82 }
83
84 private static void bindParameterMarkerProjection(final SubquerySegment boundedSubquerySegment, final IdentifierValue subqueryTableName) {
85 SelectStatement boundedSelect = boundedSubquerySegment.getSelect();
86 Collection<ProjectionSegment> projections = new LinkedList<>(boundedSelect.getProjections().getProjections());
87 boundedSelect.getProjections().getProjections().clear();
88 for (ProjectionSegment each : projections) {
89 if (!(each instanceof ParameterMarkerExpressionSegment)) {
90 boundedSelect.getProjections().getProjections().add(each);
91 continue;
92 }
93 ParameterMarkerExpressionSegment parameterMarkerProjection = (ParameterMarkerExpressionSegment) each;
94
95 boundedSelect.getProjections().getProjections().add(ParameterMarkerExpressionSegmentBinder.bind(parameterMarkerProjection, Collections.singletonMap(parameterMarkerProjection,
96 new ColumnSegmentBoundedInfo(new IdentifierValue(""), new IdentifierValue(""), subqueryTableName, parameterMarkerProjection.getAlias().orElseGet(() -> new IdentifierValue(""))))));
97 }
98 }
99
100 private static void fillPivotColumnNamesInBinderContext(final SubqueryTableSegment segment, final SQLStatementBinderContext statementBinderContext) {
101 segment.getPivot().ifPresent(optional -> optional.getPivotColumns().forEach(each -> statementBinderContext.getPivotColumnNames().add(each.getIdentifier().getValue().toLowerCase())));
102 }
103
104 private static Collection<ProjectionSegment> createSubqueryProjections(final Collection<ProjectionSegment> projections, final IdentifierValue subqueryTableName, final DatabaseType databaseType) {
105 Collection<ProjectionSegment> result = new LinkedList<>();
106 for (ProjectionSegment each : projections) {
107 if (each instanceof ColumnProjectionSegment) {
108 result.add(createColumnProjection((ColumnProjectionSegment) each, subqueryTableName));
109 } else if (each instanceof ShorthandProjectionSegment) {
110 result.addAll(createSubqueryProjections(((ShorthandProjectionSegment) each).getActualProjectionSegments(), subqueryTableName, databaseType));
111 } else if (each instanceof ExpressionProjectionSegment) {
112 result.add(createColumnProjection((ExpressionProjectionSegment) each, subqueryTableName, databaseType));
113 } else if (each instanceof AggregationProjectionSegment) {
114 result.add(createColumnProjection((AggregationProjectionSegment) each, subqueryTableName, databaseType));
115 } else {
116 result.add(each);
117 }
118 }
119 return result;
120 }
121
122 private static ColumnProjectionSegment createColumnProjection(final ColumnProjectionSegment originalColumn, final IdentifierValue subqueryTableName) {
123 ColumnSegment newColumnSegment = new ColumnSegment(0, 0, originalColumn.getAlias().orElseGet(() -> originalColumn.getColumn().getIdentifier()));
124 if (!Strings.isNullOrEmpty(subqueryTableName.getValue())) {
125 newColumnSegment.setOwner(new OwnerSegment(0, 0, subqueryTableName));
126 }
127 newColumnSegment.setColumnBoundedInfo(
128 new ColumnSegmentBoundedInfo(originalColumn.getColumn().getColumnBoundedInfo().getOriginalDatabase(), originalColumn.getColumn().getColumnBoundedInfo().getOriginalSchema(),
129 originalColumn.getColumn().getColumnBoundedInfo().getOriginalTable(), originalColumn.getColumn().getColumnBoundedInfo().getOriginalColumn()));
130 ColumnProjectionSegment result = new ColumnProjectionSegment(newColumnSegment);
131 result.setVisible(originalColumn.isVisible());
132 return result;
133 }
134
135 private static ColumnProjectionSegment createColumnProjection(final ExpressionSegment expressionSegment, final IdentifierValue subqueryTableName, final DatabaseType databaseType) {
136 ColumnSegment newColumnSegment = new ColumnSegment(0, 0,
137 new IdentifierValue(getColumnNameFromExpression(expressionSegment, databaseType), new DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getQuoteCharacter()));
138 if (!Strings.isNullOrEmpty(subqueryTableName.getValue())) {
139 newColumnSegment.setOwner(new OwnerSegment(0, 0, subqueryTableName));
140 }
141 ColumnProjectionSegment result = new ColumnProjectionSegment(newColumnSegment);
142 result.setVisible(true);
143 return result;
144 }
145
146 private static String getColumnNameFromExpression(final ExpressionSegment expressionSegment, final DatabaseType databaseType) {
147 String result;
148 if (expressionSegment instanceof AliasAvailable && ((AliasAvailable) expressionSegment).getAlias().isPresent()) {
149 result = ProjectionUtils.getColumnLabelFromAlias(((AliasAvailable) expressionSegment).getAlias().get(), databaseType);
150 } else {
151 result = ProjectionUtils.getColumnNameFromExpression(expressionSegment.getText(), databaseType);
152 }
153 return result;
154 }
155 }