1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shardingsphere.infra.binder.context.segment.select.projection.engine;
19
20 import lombok.RequiredArgsConstructor;
21 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.DerivedColumn;
22 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.Projection;
23 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.AggregationDistinctProjection;
24 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.AggregationProjection;
25 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.ColumnProjection;
26 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.ExpressionProjection;
27 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.ParameterMarkerProjection;
28 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.ShorthandProjection;
29 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.SubqueryProjection;
30 import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
31 import org.apache.shardingsphere.sql.parser.statement.core.enums.AggregationType;
32 import org.apache.shardingsphere.sql.parser.statement.core.enums.Paren;
33 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.simple.ParameterMarkerExpressionSegment;
34 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.AggregationDistinctProjectionSegment;
35 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.AggregationProjectionSegment;
36 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ColumnProjectionSegment;
37 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ExpressionProjectionSegment;
38 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionSegment;
39 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ShorthandProjectionSegment;
40 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.SubqueryProjectionSegment;
41 import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.OwnerSegment;
42 import org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
43
44 import java.util.Collection;
45 import java.util.LinkedHashSet;
46 import java.util.Optional;
47
48
49
50
51 @RequiredArgsConstructor
52 public final class ProjectionEngine {
53
54 private final DatabaseType databaseType;
55
56 private int aggregationAverageDerivedColumnCount;
57
58 private int aggregationDistinctDerivedColumnCount;
59
60
61
62
63
64
65
66 public Optional<Projection> createProjection(final ProjectionSegment projectionSegment) {
67 if (projectionSegment instanceof ShorthandProjectionSegment) {
68 return Optional.of(createProjection((ShorthandProjectionSegment) projectionSegment));
69 }
70 if (projectionSegment instanceof ColumnProjectionSegment) {
71 return Optional.of(createProjection((ColumnProjectionSegment) projectionSegment));
72 }
73 if (projectionSegment instanceof ExpressionProjectionSegment) {
74 return Optional.of(createProjection((ExpressionProjectionSegment) projectionSegment));
75 }
76 if (projectionSegment instanceof AggregationDistinctProjectionSegment) {
77 return Optional.of(createProjection((AggregationDistinctProjectionSegment) projectionSegment));
78 }
79 if (projectionSegment instanceof AggregationProjectionSegment) {
80 return Optional.of(createProjection((AggregationProjectionSegment) projectionSegment));
81 }
82 if (projectionSegment instanceof SubqueryProjectionSegment) {
83 return Optional.of(createProjection((SubqueryProjectionSegment) projectionSegment));
84 }
85 if (projectionSegment instanceof ParameterMarkerExpressionSegment) {
86 return Optional.of(createProjection((ParameterMarkerExpressionSegment) projectionSegment));
87 }
88 return Optional.empty();
89 }
90
91 private ParameterMarkerProjection createProjection(final ParameterMarkerExpressionSegment projectionSegment) {
92 return new ParameterMarkerProjection(projectionSegment.getParameterMarkerIndex(), projectionSegment.getParameterMarkerType(), projectionSegment.getAlias().orElse(null));
93 }
94
95 private SubqueryProjection createProjection(final SubqueryProjectionSegment projectionSegment) {
96 Projection subqueryProjection = createProjection(projectionSegment.getSubquery().getSelect().getProjections().getProjections().iterator().next())
97 .orElseThrow(() -> new IllegalArgumentException("Subquery projection must have at least one projection column."));
98 return new SubqueryProjection(projectionSegment, subqueryProjection, projectionSegment.getAlias().orElse(null), databaseType);
99 }
100
101 private ShorthandProjection createProjection(final ShorthandProjectionSegment projectionSegment) {
102 IdentifierValue owner = projectionSegment.getOwner().map(OwnerSegment::getIdentifier).orElse(null);
103 Collection<Projection> projections = new LinkedHashSet<>(projectionSegment.getActualProjectionSegments().size(), 1F);
104 projectionSegment.getActualProjectionSegments().forEach(each -> createProjection(each).ifPresent(projections::add));
105 return new ShorthandProjection(owner, projections);
106 }
107
108 private ColumnProjection createProjection(final ColumnProjectionSegment projectionSegment) {
109 IdentifierValue owner = projectionSegment.getColumn().getOwner().isPresent() ? projectionSegment.getColumn().getOwner().get().getIdentifier() : null;
110 IdentifierValue alias = projectionSegment.getAliasName().isPresent() ? projectionSegment.getAlias().orElse(null) : null;
111 return new ColumnProjection(owner, projectionSegment.getColumn().getIdentifier(), alias, databaseType,
112 projectionSegment.getColumn().getLeftParentheses().orElse(null), projectionSegment.getColumn().getRightParentheses().orElse(null), projectionSegment.getColumn().getColumnBoundInfo());
113 }
114
115 private ExpressionProjection createProjection(final ExpressionProjectionSegment projectionSegment) {
116 return new ExpressionProjection(projectionSegment, projectionSegment.getAlias().orElse(null), databaseType);
117 }
118
119 private AggregationDistinctProjection createProjection(final AggregationDistinctProjectionSegment projectionSegment) {
120 IdentifierValue alias =
121 projectionSegment.getAlias().orElseGet(() -> new IdentifierValue(DerivedColumn.AGGREGATION_DISTINCT_DERIVED.getDerivedColumnAlias(aggregationDistinctDerivedColumnCount++)));
122 AggregationDistinctProjection result = new AggregationDistinctProjection(
123 projectionSegment.getStartIndex(), projectionSegment.getStopIndex(), projectionSegment.getType(), projectionSegment, alias,
124 projectionSegment.getDistinctInnerExpression(), databaseType, projectionSegment.getSeparator().orElse(null));
125 if (AggregationType.AVG == result.getType()) {
126 appendAverageDistinctDerivedProjection(result);
127 }
128 return result;
129 }
130
131 private AggregationProjection createProjection(final AggregationProjectionSegment projectionSegment) {
132 AggregationProjection result =
133 new AggregationProjection(projectionSegment.getType(), projectionSegment, projectionSegment.getAlias().orElse(null), databaseType,
134 projectionSegment.getSeparator().orElse(null));
135 if (AggregationType.AVG == result.getType()) {
136 appendAverageDerivedProjection(result);
137
138 }
139 return result;
140 }
141
142 private void appendAverageDistinctDerivedProjection(final AggregationDistinctProjection averageDistinctProjection) {
143 String distinctInnerExpression = averageDistinctProjection.getDistinctInnerExpression();
144 String countAlias = DerivedColumn.AVG_COUNT_ALIAS.getDerivedColumnAlias(aggregationAverageDerivedColumnCount);
145 String innerExpression = averageDistinctProjection.getExpression().substring(averageDistinctProjection.getExpression().indexOf(Paren.PARENTHESES.getLeftParen()));
146 AggregationProjectionSegment countExpression = new AggregationProjectionSegment(0, 0, AggregationType.COUNT, AggregationType.COUNT.name() + innerExpression);
147 AggregationDistinctProjection countDistinctProjection =
148 new AggregationDistinctProjection(0, 0, AggregationType.COUNT, countExpression, new IdentifierValue(countAlias), distinctInnerExpression, databaseType);
149 String sumAlias = DerivedColumn.AVG_SUM_ALIAS.getDerivedColumnAlias(aggregationAverageDerivedColumnCount);
150 AggregationProjectionSegment sumExpression = new AggregationProjectionSegment(0, 0, AggregationType.SUM, AggregationType.SUM.name() + innerExpression);
151 AggregationDistinctProjection sumDistinctProjection =
152 new AggregationDistinctProjection(0, 0, AggregationType.SUM, sumExpression, new IdentifierValue(sumAlias), distinctInnerExpression, databaseType);
153 averageDistinctProjection.getDerivedAggregationProjections().add(countDistinctProjection);
154 averageDistinctProjection.getDerivedAggregationProjections().add(sumDistinctProjection);
155 aggregationAverageDerivedColumnCount++;
156 }
157
158 private void appendAverageDerivedProjection(final AggregationProjection averageProjection) {
159 String countAlias = DerivedColumn.AVG_COUNT_ALIAS.getDerivedColumnAlias(aggregationAverageDerivedColumnCount);
160 String innerExpression = averageProjection.getExpression().substring(averageProjection.getExpression().indexOf(Paren.PARENTHESES.getLeftParen()));
161 AggregationProjectionSegment countExpression = new AggregationProjectionSegment(0, 0, AggregationType.COUNT, AggregationType.COUNT.name() + innerExpression);
162 AggregationProjection countProjection = new AggregationProjection(AggregationType.COUNT, countExpression, new IdentifierValue(countAlias), databaseType);
163 String sumAlias = DerivedColumn.AVG_SUM_ALIAS.getDerivedColumnAlias(aggregationAverageDerivedColumnCount);
164 AggregationProjectionSegment sumExpression = new AggregationProjectionSegment(0, 0, AggregationType.SUM, AggregationType.SUM.name() + innerExpression);
165 AggregationProjection sumProjection = new AggregationProjection(AggregationType.SUM, sumExpression, new IdentifierValue(sumAlias), databaseType);
166 averageProjection.getDerivedAggregationProjections().add(countProjection);
167 averageProjection.getDerivedAggregationProjections().add(sumProjection);
168 aggregationAverageDerivedColumnCount++;
169 }
170 }