View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shardingsphere.encrypt.merge.dal.show;
19  
20  import org.apache.shardingsphere.encrypt.exception.syntax.UnsupportedEncryptSQLException;
21  import org.apache.shardingsphere.encrypt.rule.EncryptRule;
22  import org.apache.shardingsphere.encrypt.rule.EncryptTable;
23  import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
24  import org.apache.shardingsphere.infra.binder.context.type.TableAvailable;
25  import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
26  import org.apache.shardingsphere.infra.merge.result.MergedResult;
27  import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
28  import org.apache.shardingsphere.infra.parser.SQLParserEngine;
29  import org.apache.shardingsphere.parser.rule.SQLParserRule;
30  import org.apache.shardingsphere.sql.parser.sql.common.segment.ddl.column.ColumnDefinitionSegment;
31  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.column.ColumnSegment;
32  import org.apache.shardingsphere.sql.parser.sql.common.statement.ddl.CreateTableStatement;
33  
34  import java.io.InputStream;
35  import java.io.Reader;
36  import java.sql.SQLException;
37  import java.sql.SQLFeatureNotSupportedException;
38  import java.util.ArrayList;
39  import java.util.Calendar;
40  import java.util.List;
41  import java.util.Optional;
42  
43  /**
44   * Encrypt show create table merged result.
45   */
46  public abstract class EncryptShowCreateTableMergedResult implements MergedResult {
47      
48      private static final String COMMA = ", ";
49      
50      private static final int CREATE_TABLE_DEFINITION_INDEX = 2;
51      
52      private final String tableName;
53      
54      private final EncryptRule encryptRule;
55      
56      private final SQLParserEngine sqlParserEngine;
57      
58      protected EncryptShowCreateTableMergedResult(final RuleMetaData globalRuleMetaData, final SQLStatementContext sqlStatementContext, final EncryptRule encryptRule) {
59          ShardingSpherePreconditions.checkState(sqlStatementContext instanceof TableAvailable && 1 == ((TableAvailable) sqlStatementContext).getAllTables().size(),
60                  () -> new UnsupportedEncryptSQLException("SHOW CREATE TABLE FOR MULTI TABLE"));
61          tableName = ((TableAvailable) sqlStatementContext).getAllTables().iterator().next().getTableName().getIdentifier().getValue();
62          this.encryptRule = encryptRule;
63          sqlParserEngine = globalRuleMetaData.getSingleRule(SQLParserRule.class).getSQLParserEngine(sqlStatementContext.getDatabaseType());
64      }
65      
66      @Override
67      public final boolean next() throws SQLException {
68          return nextValue();
69      }
70      
71      @Override
72      public final Object getValue(final int columnIndex, final Class<?> type) throws SQLException {
73          if (CREATE_TABLE_DEFINITION_INDEX == columnIndex) {
74              String result = getOriginalValue(CREATE_TABLE_DEFINITION_INDEX, type).toString();
75              Optional<EncryptTable> encryptTable = encryptRule.findEncryptTable(tableName);
76              if (!encryptTable.isPresent() || !result.contains("(")) {
77                  return result;
78              }
79              CreateTableStatement createTableStatement = (CreateTableStatement) sqlParserEngine.parse(result, false);
80              List<ColumnDefinitionSegment> columnDefinitions = new ArrayList<>(createTableStatement.getColumnDefinitions());
81              StringBuilder builder = new StringBuilder(result.substring(0, columnDefinitions.get(0).getStartIndex()));
82              for (ColumnDefinitionSegment each : columnDefinitions) {
83                  findLogicColumnDefinition(each, encryptTable.get(), result).ifPresent(optional -> builder.append(optional).append(COMMA));
84              }
85              // TODO decorate encrypt column index when we support index rewrite
86              builder.delete(builder.length() - COMMA.length(), builder.length()).append(result.substring(columnDefinitions.get(columnDefinitions.size() - 1).getStopIndex() + 1));
87              return builder.toString();
88          }
89          return getOriginalValue(columnIndex, type);
90      }
91      
92      private Optional<String> findLogicColumnDefinition(final ColumnDefinitionSegment columnDefinition, final EncryptTable encryptTable, final String sql) {
93          ColumnSegment columnSegment = columnDefinition.getColumnName();
94          String columnName = columnSegment.getIdentifier().getValue();
95          if (encryptTable.isCipherColumn(columnName)) {
96              String logicColumn = encryptTable.getLogicColumnByCipherColumn(columnName);
97              return Optional.of(sql.substring(columnDefinition.getStartIndex(), columnSegment.getStartIndex())
98                      + columnSegment.getIdentifier().getQuoteCharacter().wrap(logicColumn) + sql.substring(columnSegment.getStopIndex() + 1, columnDefinition.getStopIndex() + 1));
99          }
100         if (isDerivedColumn(encryptTable, columnName)) {
101             return Optional.empty();
102         }
103         return Optional.of(sql.substring(columnDefinition.getStartIndex(), columnDefinition.getStopIndex() + 1));
104     }
105     
106     private boolean isDerivedColumn(final EncryptTable encryptTable, final String columnName) {
107         return encryptTable.isAssistedQueryColumn(columnName) || encryptTable.isLikeQueryColumn(columnName);
108     }
109     
110     @Override
111     public final Object getCalendarValue(final int columnIndex, final Class<?> type, final Calendar calendar) throws SQLException {
112         throw new SQLFeatureNotSupportedException("");
113     }
114     
115     @Override
116     public final InputStream getInputStream(final int columnIndex, final String type) throws SQLException {
117         throw new SQLFeatureNotSupportedException("");
118     }
119     
120     @Override
121     public Reader getCharacterStream(final int columnIndex) throws SQLException {
122         throw new SQLFeatureNotSupportedException("");
123     }
124     
125     protected abstract boolean nextValue() throws SQLException;
126     
127     protected abstract Object getOriginalValue(int columnIndex, Class<?> type) throws SQLException;
128 }