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.sql.common.enums.AggregationType;
32 import org.apache.shardingsphere.sql.parser.sql.common.enums.Paren;
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.item.AggregationDistinctProjectionSegment;
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.dml.item.SubqueryProjectionSegment;
41 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.OwnerSegment;
42 import org.apache.shardingsphere.sql.parser.sql.common.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<>();
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 ColumnProjection result = new ColumnProjection(owner, projectionSegment.getColumn().getIdentifier(), alias, databaseType);
112 result.setOriginalColumn(projectionSegment.getColumn().getColumnBoundedInfo().getOriginalColumn());
113 result.setOriginalTable(projectionSegment.getColumn().getColumnBoundedInfo().getOriginalTable());
114 return result;
115 }
116
117 private ExpressionProjection createProjection(final ExpressionProjectionSegment projectionSegment) {
118 return new ExpressionProjection(projectionSegment, projectionSegment.getAlias().orElse(null), databaseType);
119 }
120
121 private AggregationDistinctProjection createProjection(final AggregationDistinctProjectionSegment projectionSegment) {
122 IdentifierValue alias =
123 projectionSegment.getAlias().orElseGet(() -> new IdentifierValue(DerivedColumn.AGGREGATION_DISTINCT_DERIVED.getDerivedColumnAlias(aggregationDistinctDerivedColumnCount++)));
124 AggregationDistinctProjection result = new AggregationDistinctProjection(
125 projectionSegment.getStartIndex(), projectionSegment.getStopIndex(), projectionSegment.getType(), projectionSegment.getExpression(), alias,
126 projectionSegment.getDistinctInnerExpression(), databaseType);
127 if (AggregationType.AVG == result.getType()) {
128 appendAverageDistinctDerivedProjection(result);
129 }
130 return result;
131 }
132
133 private AggregationProjection createProjection(final AggregationProjectionSegment projectionSegment) {
134 AggregationProjection result = new AggregationProjection(projectionSegment.getType(), projectionSegment.getExpression(), projectionSegment.getAlias().orElse(null), databaseType);
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 AggregationDistinctProjection countDistinctProjection =
147 new AggregationDistinctProjection(0, 0, AggregationType.COUNT, AggregationType.COUNT.name() + innerExpression, new IdentifierValue(countAlias), distinctInnerExpression, databaseType);
148 String sumAlias = DerivedColumn.AVG_SUM_ALIAS.getDerivedColumnAlias(aggregationAverageDerivedColumnCount);
149 AggregationDistinctProjection sumDistinctProjection =
150 new AggregationDistinctProjection(0, 0, AggregationType.SUM, AggregationType.SUM.name() + innerExpression, new IdentifierValue(sumAlias), distinctInnerExpression, databaseType);
151 averageDistinctProjection.getDerivedAggregationProjections().add(countDistinctProjection);
152 averageDistinctProjection.getDerivedAggregationProjections().add(sumDistinctProjection);
153 aggregationAverageDerivedColumnCount++;
154 }
155
156 private void appendAverageDerivedProjection(final AggregationProjection averageProjection) {
157 String countAlias = DerivedColumn.AVG_COUNT_ALIAS.getDerivedColumnAlias(aggregationAverageDerivedColumnCount);
158 String innerExpression = averageProjection.getExpression().substring(averageProjection.getExpression().indexOf(Paren.PARENTHESES.getLeftParen()));
159 AggregationProjection countProjection = new AggregationProjection(AggregationType.COUNT, AggregationType.COUNT.name() + innerExpression, new IdentifierValue(countAlias), databaseType);
160 String sumAlias = DerivedColumn.AVG_SUM_ALIAS.getDerivedColumnAlias(aggregationAverageDerivedColumnCount);
161 AggregationProjection sumProjection = new AggregationProjection(AggregationType.SUM, AggregationType.SUM.name() + innerExpression, new IdentifierValue(sumAlias), databaseType);
162 averageProjection.getDerivedAggregationProjections().add(countProjection);
163 averageProjection.getDerivedAggregationProjections().add(sumProjection);
164 aggregationAverageDerivedColumnCount++;
165 }
166 }