1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shardingsphere.infra.rewrite.sql.token.pojo.generic;
19
20 import com.cedarsoftware.util.CaseInsensitiveMap;
21 import lombok.EqualsAndHashCode;
22 import lombok.Getter;
23 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.Projection;
24 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.ColumnProjection;
25 import org.apache.shardingsphere.infra.database.core.metadata.database.enums.QuoteCharacter;
26 import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
27 import org.apache.shardingsphere.infra.database.core.type.DatabaseTypeRegistry;
28 import org.apache.shardingsphere.infra.rewrite.sql.token.pojo.RouteUnitAware;
29 import org.apache.shardingsphere.infra.rewrite.sql.token.pojo.SQLToken;
30 import org.apache.shardingsphere.infra.rewrite.sql.token.pojo.Substitutable;
31 import org.apache.shardingsphere.infra.route.context.RouteMapper;
32 import org.apache.shardingsphere.infra.route.context.RouteUnit;
33 import org.apache.shardingsphere.sql.parser.sql.common.value.identifier.IdentifierValue;
34
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.Map;
39
40
41
42
43 @EqualsAndHashCode(callSuper = false)
44 public final class SubstitutableColumnNameToken extends SQLToken implements Substitutable, RouteUnitAware {
45
46 private static final String COLUMN_NAME_SPLITTER = ", ";
47
48 @Getter
49 private final int stopIndex;
50
51 private final Collection<Projection> projections;
52
53 private final boolean lastColumn;
54
55 private final QuoteCharacter quoteCharacter;
56
57 public SubstitutableColumnNameToken(final int startIndex, final int stopIndex, final Collection<Projection> projections, final DatabaseType databaseType) {
58 super(startIndex);
59 this.stopIndex = stopIndex;
60 this.lastColumn = false;
61 this.quoteCharacter = new DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getQuoteCharacter();
62 this.projections = projections;
63 }
64
65 @Override
66 public String toString(final RouteUnit routeUnit) {
67 Map<String, String> logicAndActualTables = new HashMap<>();
68 if (null != routeUnit) {
69 logicAndActualTables.putAll(getLogicAndActualTables(routeUnit));
70 }
71 StringBuilder result = new StringBuilder();
72 int count = 0;
73 for (Projection each : projections) {
74 if (0 == count && !lastColumn) {
75 result.append(getColumnExpression(each, logicAndActualTables));
76 } else {
77 result.append(COLUMN_NAME_SPLITTER).append(getColumnExpression(each, logicAndActualTables));
78 }
79 count++;
80 }
81 return result.toString();
82 }
83
84 private Map<String, String> getLogicAndActualTables(final RouteUnit routeUnit) {
85 if (null == routeUnit) {
86 return Collections.emptyMap();
87 }
88 Map<String, String> result = new CaseInsensitiveMap<>();
89 for (RouteMapper each : routeUnit.getTableMappers()) {
90 result.put(each.getLogicName().toLowerCase(), each.getActualName());
91 }
92 return result;
93 }
94
95 private String getColumnExpression(final Projection projection, final Map<String, String> logicActualTableNames) {
96 StringBuilder builder = new StringBuilder();
97 if (projection instanceof ColumnProjection) {
98 appendColumnProjection((ColumnProjection) projection, logicActualTableNames, builder);
99 } else {
100 builder.append(quoteCharacter.wrap(projection.getColumnLabel()));
101 }
102 return builder.toString();
103 }
104
105 private void appendColumnProjection(final ColumnProjection columnProjection, final Map<String, String> logicActualTableNames, final StringBuilder builder) {
106 if (columnProjection.getOwner().isPresent()) {
107 IdentifierValue owner = columnProjection.getOwner().get();
108 String actualTableOwner = logicActualTableNames.getOrDefault(owner.getValue(), owner.getValue());
109 builder.append(getValueWithQuoteCharacters(new IdentifierValue(actualTableOwner, owner.getQuoteCharacter()))).append('.');
110 }
111 builder.append(getValueWithQuoteCharacters(columnProjection.getName()));
112 if (columnProjection.getAlias().isPresent()) {
113 builder.append(" AS ").append(getValueWithQuoteCharacters(columnProjection.getAlias().get()));
114 }
115 }
116
117 private String getValueWithQuoteCharacters(final IdentifierValue identifierValue) {
118 return QuoteCharacter.NONE == identifierValue.getQuoteCharacter() ? identifierValue.getValue() : quoteCharacter.wrap(identifierValue.getValue());
119 }
120 }