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.proxy.backend.hbase.converter.type;
19  
20  import com.google.common.base.Preconditions;
21  import lombok.RequiredArgsConstructor;
22  import org.apache.hadoop.hbase.client.Get;
23  import org.apache.hadoop.hbase.client.Query;
24  import org.apache.hadoop.hbase.client.Scan;
25  import org.apache.hadoop.hbase.util.Bytes;
26  import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
27  import org.apache.shardingsphere.infra.binder.context.statement.dml.SelectStatementContext;
28  import org.apache.shardingsphere.proxy.backend.hbase.bean.HBaseOperation;
29  import org.apache.shardingsphere.proxy.backend.hbase.converter.HBaseOperationConverter;
30  import org.apache.shardingsphere.proxy.backend.hbase.converter.HBaseRowKeyExtractor;
31  import org.apache.shardingsphere.proxy.backend.hbase.converter.operation.HBaseSelectOperation;
32  import org.apache.shardingsphere.proxy.backend.hbase.util.HBaseHeterogeneousUtils;
33  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.BetweenExpression;
34  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.BinaryOperationExpression;
35  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExpressionSegment;
36  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.InExpression;
37  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.simple.LiteralExpressionSegment;
38  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.pagination.limit.NumberLiteralLimitValueSegment;
39  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.predicate.WhereSegment;
40  import org.apache.shardingsphere.sql.parser.sql.dialect.statement.mysql.dml.MySQLSelectStatement;
41  
42  import java.util.Arrays;
43  import java.util.Collections;
44  import java.util.List;
45  import java.util.Optional;
46  import java.util.stream.Collectors;
47  
48  /**
49   * HBase select operation converter.
50   */
51  @RequiredArgsConstructor
52  public final class HBaseSelectOperationConverter implements HBaseOperationConverter {
53      
54      private final SQLStatementContext sqlStatementContext;
55      
56      @Override
57      public HBaseOperation convert() {
58          SelectStatementContext selectStatementContext = (SelectStatementContext) sqlStatementContext;
59          Optional<WhereSegment> whereSegment = selectStatementContext.getWhereSegments().stream().findFirst();
60          Preconditions.checkArgument(whereSegment.isPresent(), "Where segment is absent.");
61          return whereSegment.get().getExpr() instanceof BinaryOperationExpression || whereSegment.get().getExpr() instanceof InExpression
62                  ? createGetOperation(selectStatementContext, whereSegment.get())
63                  : createScanOperation(selectStatementContext, whereSegment.get());
64      }
65      
66      private HBaseOperation createGetOperation(final SelectStatementContext selectStatementContext, final WhereSegment whereSegment) {
67          ExpressionSegment expr = whereSegment.getExpr();
68          List<Get> gets = getRowKeys(expr).stream().map(each -> new Get(Bytes.toBytes(each))).collect(Collectors.toList());
69          if (!HBaseHeterogeneousUtils.isUseShorthandProjection(selectStatementContext)) {
70              for (Get each : gets) {
71                  appendColumns(each, selectStatementContext);
72              }
73          }
74          String tableName = selectStatementContext.getTablesContext().getTableNames().iterator().next();
75          return expr instanceof InExpression ? new HBaseOperation(tableName, new HBaseSelectOperation(gets)) : new HBaseOperation(tableName, gets.get(0));
76      }
77      
78      private List<String> getRowKeys(final ExpressionSegment expr) {
79          return expr instanceof InExpression ? HBaseRowKeyExtractor.getRowKeys((InExpression) expr) : Collections.singletonList(HBaseRowKeyExtractor.getRowKey((BinaryOperationExpression) expr));
80      }
81      
82      private HBaseOperation createScanOperation(final SelectStatementContext selectStatementContext, final WhereSegment whereSegment) {
83          Scan scan = new Scan();
84          if (whereSegment.getExpr() instanceof BetweenExpression) {
85              appendBetween(scan, whereSegment.getExpr(), false);
86          }
87          if (!HBaseHeterogeneousUtils.isUseShorthandProjection(selectStatementContext)) {
88              appendColumns(scan, selectStatementContext);
89          }
90          appendLimit(scan, selectStatementContext);
91          return new HBaseOperation(selectStatementContext.getTablesContext().getTableNames().iterator().next(), scan);
92      }
93      
94      private void appendColumns(final Query query, final SelectStatementContext selectStatementContext) {
95          if (query instanceof Get) {
96              selectStatementContext.getColumnSegments().forEach(each -> ((Get) query).addColumn(Bytes.toBytes("i"), Bytes.toBytes(String.valueOf(each))));
97          } else {
98              selectStatementContext.getColumnSegments().forEach(each -> ((Scan) query).addColumn(Bytes.toBytes("i"), Bytes.toBytes(String.valueOf(each))));
99          }
100     }
101     
102     private void appendBetween(final Scan scan, final ExpressionSegment expressionSegment, final boolean reversed) {
103         BetweenExpression betweenExpr = (BetweenExpression) expressionSegment;
104         String startRowKey = ((LiteralExpressionSegment) betweenExpr.getBetweenExpr()).getLiterals().toString();
105         String stopRowKey = ((LiteralExpressionSegment) betweenExpr.getAndExpr()).getLiterals().toString();
106         if (null != startRowKey && null != stopRowKey) {
107             if (reversed) {
108                 scan.withStopRow(calBytes(startRowKey, 0), true);
109                 // refer: <https://github.com/apache/hbase/blob/master/hbase-backup/src/main/java/org/apache/hadoop/hbase/backup/impl/BackupSystemTable.java#L1853>
110                 scan.withStartRow(calBytes(stopRowKey + "~", 0), true);
111             } else {
112                 scan.withStartRow(calBytes(startRowKey, 0), true);
113                 scan.withStopRow(calBytes(stopRowKey + "~", 0), true);
114             }
115         }
116     }
117     
118     private byte[] calBytes(final String row, final int step) {
119         byte[] rowByte = Bytes.toBytes(row);
120         byte[] result = Arrays.copyOf(rowByte, rowByte.length);
121         result[result.length - 1] = (byte) (result[result.length - 1] + step);
122         return result;
123     }
124     
125     private void appendLimit(final Scan scan, final SelectStatementContext selectStatementContext) {
126         // TODO consider about other dialect
127         MySQLSelectStatement selectStatement = (MySQLSelectStatement) selectStatementContext.getSqlStatement();
128         if (selectStatement.getLimit().isPresent()) {
129             selectStatement.getLimit().get().getRowCount().ifPresent(optional -> scan.setLimit((int) ((NumberLiteralLimitValueSegment) optional).getValue()));
130         }
131     }
132 }