View Javadoc
1   /*
2    * Copyright 2021 TiKV Project Authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   */
17  
18  package org.tikv.common.predicates;
19  
20  import static java.util.Objects.requireNonNull;
21  import static org.tikv.common.predicates.PredicateUtils.mergeCNFExpressions;
22  
23  import com.google.common.collect.ImmutableList;
24  import java.util.ArrayList;
25  import java.util.HashSet;
26  import java.util.IdentityHashMap;
27  import java.util.List;
28  import java.util.Optional;
29  import java.util.Set;
30  import org.tikv.common.exception.TiExpressionException;
31  import org.tikv.common.expression.Expression;
32  import org.tikv.common.meta.TiColumnInfo;
33  import org.tikv.common.meta.TiIndexColumn;
34  import org.tikv.common.meta.TiIndexInfo;
35  import org.tikv.common.meta.TiTableInfo;
36  import org.tikv.common.types.DataType;
37  
38  public class ScanSpec {
39    private final List<Expression> pointPredicates;
40    private final Optional<Expression> rangePredicate;
41    private final Set<Expression> residualPredicates;
42  
43    private ScanSpec(
44        List<Expression> pointPredicates,
45        Optional<Expression> rangePredicate,
46        Set<Expression> residualPredicates) {
47      this.pointPredicates = pointPredicates;
48      this.rangePredicate = rangePredicate;
49      this.residualPredicates = residualPredicates;
50    }
51  
52    public List<Expression> getPointPredicates() {
53      return pointPredicates;
54    }
55  
56    public Optional<Expression> getRangePredicate() {
57      return rangePredicate;
58    }
59  
60    public Set<Expression> getResidualPredicates() {
61      return residualPredicates;
62    }
63  
64    public static class Builder {
65      private final IdentityHashMap<TiIndexColumn, List<Expression>> pointPredicates =
66          new IdentityHashMap<>();
67      private final TiTableInfo table;
68      private final TiIndexInfo index;
69      private final List<Expression> rangePredicates = new ArrayList<>();
70      private final List<Expression> residualPredicates = new ArrayList<>();
71      private final Set<Expression> residualCandidates = new HashSet<>();
72      private TiIndexColumn rangeColumn;
73  
74      public Builder(TiTableInfo table, TiIndexInfo index) {
75        this.table = table;
76        this.index = index;
77      }
78  
79      void addResidualPredicate(Expression predicate) {
80        residualPredicates.add(predicate);
81      }
82  
83      void addAllPredicates(List<Expression> predicates) {
84        residualCandidates.addAll(predicates);
85      }
86  
87      void addPointPredicate(TiIndexColumn col, Expression predicate) {
88        requireNonNull(col, "index column is null");
89        requireNonNull(predicate, "predicate is null");
90        if (pointPredicates.containsKey(col)) {
91          List<Expression> predicates = pointPredicates.get(col);
92          predicates.add(predicate);
93        } else {
94          List<Expression> predicates = new ArrayList<>();
95          predicates.add(predicate);
96          pointPredicates.put(col, predicates);
97        }
98      }
99  
100     void addRangePredicate(TiIndexColumn col, Expression predicate) {
101       requireNonNull(col, "col is null");
102       if (rangeColumn == null) {
103         rangeColumn = col;
104       } else if (!rangeColumn.equals(col)) {
105         throw new TiExpressionException("Cannot reset range predicates");
106       }
107       rangePredicates.add(predicate);
108     }
109 
110     public ScanSpec build() {
111       List<Expression> points = new ArrayList<>();
112       List<DataType> pointTypes = new ArrayList<>();
113       Set<Expression> pushedPredicates = new HashSet<>();
114       if (index != null) {
115         for (TiIndexColumn indexColumn : index.getIndexColumns()) {
116           List<Expression> predicates = pointPredicates.get(indexColumn);
117           if (predicates == null) {
118             break;
119           }
120           pushedPredicates.addAll(predicates);
121           TiColumnInfo tiColumnInfo = table.getColumn(indexColumn.getOffset());
122           DataType type = tiColumnInfo.getType();
123           points.add(mergeCNFExpressions(predicates));
124           pointTypes.add(type);
125         }
126       }
127       Optional<Expression> newRangePred =
128           rangePredicates.isEmpty()
129               ? Optional.empty()
130               : Optional.ofNullable(mergeCNFExpressions(rangePredicates));
131       pushedPredicates.addAll(rangePredicates);
132 
133       Set<Expression> newResidualPredicates = new HashSet<>(residualPredicates);
134       for (Expression pred : residualCandidates) {
135         if (!pushedPredicates.contains(pred)) {
136           newResidualPredicates.add(pred);
137         }
138       }
139 
140       return new ScanSpec(ImmutableList.copyOf(points), newRangePred, newResidualPredicates);
141     }
142   }
143 }