YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/yql/cql/ql/ptree/ycql_predtest.cc
Line
Count
Source (jump to first uncovered line)
1
//--------------------------------------------------------------------------------------------------
2
// Copyright (c) YugaByte, Inc.
3
//
4
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5
// in compliance with the License.  You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software distributed under the License
10
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11
// or implied.  See the License for the specific language governing permissions and limitations
12
// under the License.
13
//
14
//--------------------------------------------------------------------------------------------------
15
16
#include "yb/yql/cql/ql/ptree/ycql_predtest.h"
17
18
#include "yb/common/common.pb.h"
19
20
#include "yb/util/result.h"
21
#include "yb/util/status_format.h"
22
23
#include "yb/yql/cql/ql/ptree/column_arg.h"
24
#include "yb/yql/cql/ql/ptree/column_desc.h"
25
#include "yb/yql/cql/ql/ptree/pt_expr.h"
26
27
namespace yb {
28
namespace ql {
29
30
// TODO(Piyush): Make WhereClauseImpliesPred() more smarter -
31
//   1. Support more complex math rules: e.g.,: v > 5 => v > 4. Right now we don't handle that.
32
33
// TODO (Piyush): To support WhereClauseImpliesPred() for arbitrary expressions, we need
34
// PTConstToPB() and that needs some refactor to be able to use here. For now we are checking only
35
// for expressions with NULLs, integers, booleans and strings.
36
37
2.12k
static Result<bool> exprMatchesColOp(const ColumnOp& col_op, const QLExpressionPB& predicate) {
38
2.12k
  int col_id_in_pred = predicate.condition().operands(0).column_id();
39
2.12k
  QLValuePB value_in_pred = predicate.condition().operands(1).value();
40
41
2.12k
  if (col_op.yb_op() != predicate.condition().op()) return false;
42
1.61k
  if (col_op.desc()->id() != col_id_in_pred) return false;
43
1.23k
  if (col_op.expr()->expr_op() != ExprOperator::kConst) return false;
44
45
  // We support only NULLs and int in index predicates for now.
46
1.23k
  switch (value_in_pred.value_case()) {
47
514
    case QLValuePB::VALUE_NOT_SET: {
48
514
      std::shared_ptr<PTNull> const_null_value =
49
514
        std::dynamic_pointer_cast<PTNull>(col_op.expr());
50
514
      if (const_null_value) return true;
51
0
      return false;
52
0
    }
53
0
    case QLValuePB::kInt64Value: {
54
0
      std::shared_ptr<PTConstInt> const_int_value =
55
0
        std::dynamic_pointer_cast<PTConstInt>(col_op.expr());
56
0
      if (const_int_value && const_int_value->value() == value_in_pred.int64_value())
57
0
        return true;
58
59
0
      int64_t value;
60
0
      std::shared_ptr<PTConstVarInt> const_varint_value =
61
0
        std::dynamic_pointer_cast<PTConstVarInt>(col_op.expr());
62
0
      if (const_varint_value) {
63
0
        if (!const_varint_value->ToInt64(&value, false).ok()) return false;
64
0
        if (value == value_in_pred.int64_value()) return true;
65
0
      }
66
0
      return false;
67
0
    }
68
528
    case QLValuePB::kInt32Value: {
69
528
      std::shared_ptr<PTConstInt> const_int_value =
70
528
        std::dynamic_pointer_cast<PTConstInt>(col_op.expr());
71
528
      if (const_int_value && const_int_value->value() == value_in_pred.int32_value())
72
0
        return true;
73
74
528
      int64_t value;
75
528
      std::shared_ptr<PTConstVarInt> const_varint_value =
76
528
        std::dynamic_pointer_cast<PTConstVarInt>(col_op.expr());
77
528
      if (const_varint_value) {
78
528
        if (!const_varint_value->ToInt64(&value, false).ok()) return false;
79
528
        if (value == value_in_pred.int32_value()) return true;
80
0
      }
81
0
      return false;
82
0
    }
83
0
    case QLValuePB::kInt16Value: {
84
0
      std::shared_ptr<PTConstInt> const_int_value =
85
0
        std::dynamic_pointer_cast<PTConstInt>(col_op.expr());
86
0
      if (const_int_value && const_int_value->value() == value_in_pred.int16_value())
87
0
        return true;
88
89
0
      int64_t value;
90
0
      std::shared_ptr<PTConstVarInt> const_varint_value =
91
0
        std::dynamic_pointer_cast<PTConstVarInt>(col_op.expr());
92
0
      if (const_varint_value) {
93
0
        if (!const_varint_value->ToInt64(&value, false).ok()) return false;
94
0
        if (value == value_in_pred.int16_value()) return true;
95
0
      }
96
0
      return false;
97
0
    }
98
0
    case QLValuePB::kInt8Value: {
99
0
      std::shared_ptr<PTConstInt> const_int_value =
100
0
        std::dynamic_pointer_cast<PTConstInt>(col_op.expr());
101
0
      if (const_int_value && const_int_value->value() == value_in_pred.int8_value())
102
0
        return true;
103
104
0
      int64_t value;
105
0
      std::shared_ptr<PTConstVarInt> const_varint_value =
106
0
        std::dynamic_pointer_cast<PTConstVarInt>(col_op.expr());
107
0
      if (const_varint_value) {
108
0
        if (!const_varint_value->ToInt64(&value, false).ok()) return false;
109
0
        if (value == value_in_pred.int8_value()) return true;
110
0
      }
111
0
      return false;
112
0
    }
113
96
    case QLValuePB::kBoolValue: {
114
96
      std::shared_ptr<PTConstBool> const_bool_value =
115
96
        std::dynamic_pointer_cast<PTConstBool>(col_op.expr());
116
96
      if (const_bool_value && const_bool_value->value() == value_in_pred.bool_value())
117
96
        return true;
118
119
0
      return false;
120
0
    }
121
96
    case QLValuePB::kStringValue: {
122
96
      std::shared_ptr<PTConstText> const_text_value =
123
96
        std::dynamic_pointer_cast<PTConstText>(col_op.expr());
124
96
      if (const_text_value && const_text_value->ToString() == value_in_pred.string_value())
125
96
        return true;
126
127
0
      return false;
128
0
    }
129
0
    default:
130
0
      return false;
131
1.23k
  }
132
1.23k
}
133
// Check if all ops in predicate are included in the col_id_ops_map i.e., whether each sub-clause
134
// in the predicate matches some op in col_id_ops_map. Since sub-clauses in predicate can only be
135
// connected by the AND operator - if this function returns true, it means all ops in col_id_ops_map
136
// if connected by AND, logically imply the predicate.
137
//
138
// Currently we are only able to check a match for ops which have =, !=, >, >=, <, <= operators on
139
// columns of the following type (if operator is applicable to the type) - TINYINT, SMALLINT, INT,
140
// BIGINT, VARINT, BOOL and TEXT.
141
static Result<bool> ExprInOps(const QLExpressionPB& predicate,
142
                              const std::unordered_map<int,
143
                                std::vector<const ColumnOp*>> &col_id_ops_map,
144
                              int *pred_len,
145
608
                              MCUnorderedMap<int32, uint16> *column_ref_cnts) {
146
608
  if (!predicate.has_condition()) {
147
0
    RSTATUS_DCHECK(false, InternalError, "Complex expressions not supported in index predicate."
148
0
      " Should have been blocked in index create itself");
149
0
  }
150
151
608
  switch (predicate.condition().op()) {
152
88
    case QL_OP_AND:
153
88
      return
154
88
        VERIFY_RESULT(ExprInOps(predicate.condition().operands(0), col_id_ops_map, pred_len,
155
88
                                column_ref_cnts)) &&
156
88
        VERIFY_RESULT(ExprInOps(predicate.condition().operands(1), col_id_ops_map, pred_len,
157
0
                                column_ref_cnts));
158
248
    case QL_OP_EQUAL:
159
328
    case QL_OP_NOT_EQUAL:
160
520
    case QL_OP_GREATER_THAN:
161
520
    case QL_OP_GREATER_THAN_EQUAL:
162
520
    case QL_OP_LESS_THAN_EQUAL:
163
520
    case QL_OP_LESS_THAN: {
164
0
      DCHECK(predicate.condition().operands(0).has_column_id()) <<
165
0
        "Complex expressions not supported in index predicate";
166
0
      DCHECK(predicate.condition().operands(1).has_value()) <<
167
0
        "Complex expressions not supported in index predicate";
168
169
520
      *pred_len = *pred_len + 1;
170
520
      int col_id_in_pred = predicate.condition().operands(0).column_id();
171
172
520
      if (col_id_ops_map.find(col_id_in_pred) == col_id_ops_map.end())
173
8
        return false;
174
175
521
      for (const ColumnOp* col_op : col_id_ops_map.at(col_id_in_pred)) {
176
521
        if (VERIFY_RESULT(exprMatchesColOp(*col_op, predicate))) {
177
505
          if (column_ref_cnts->find(col_op->desc()->id()) != column_ref_cnts->end()) {
178
505
            (*column_ref_cnts)[col_op->desc()->id()]--;
179
            // TODO(Piyush): Handle overflow errors
180
            // if (overflow)
181
            //  RSTATUS_DCHECK(false, InternalError, "Invalid state");
182
505
          }
183
505
          return true;
184
505
        }
185
521
      }
186
7
      return false;
187
512
    }
188
0
    default:
189
0
      RSTATUS_DCHECK(false, InternalError, "Complex expressions not supported in index predicate."
190
608
        " Should have been blocked in index create itself");
191
608
  }
192
608
}
193
194
Result<bool> WhereClauseImpliesPred(const MCList<ColumnOp> &col_ops,
195
                                    const QLExpressionPB& predicate,
196
                                    int *predicate_len,
197
432
                                    MCUnorderedMap<int32, uint16> *column_ref_cnts) {
198
  // Column Id to op map
199
432
  std::unordered_map<int, std::vector<const ColumnOp*>> col_id_ops_map;
200
201
939
  for (const ColumnOp& col_op : col_ops) {
202
939
    int col_id = col_op.desc()->id();
203
939
    if (col_id_ops_map.find(col_id) == col_id_ops_map.end())
204
930
      col_id_ops_map[col_id] = vector<const ColumnOp*>();
205
939
    col_id_ops_map[col_id].push_back(&col_op);
206
939
  }
207
208
432
  return ExprInOps(predicate, col_id_ops_map, predicate_len, column_ref_cnts);
209
432
}
210
211
1.94k
Result<bool> OpInExpr(const QLExpressionPB& predicate, const ColumnOp& col_op) {
212
1.94k
  if (!predicate.has_condition()) {
213
0
    RSTATUS_DCHECK(false, InternalError, "Complex expressions not supported in index predicate."
214
0
      " Should have been blocked in index create itself");
215
0
  }
216
217
1.94k
  switch (predicate.condition().op()) {
218
336
    case QL_OP_AND:
219
336
      return
220
336
        VERIFY_RESULT(OpInExpr(predicate.condition().operands(0), col_op)) ||
221
336
        VERIFY_RESULT(OpInExpr(predicate.condition().operands(1), col_op));
222
608
    case QL_OP_EQUAL:
223
903
    case QL_OP_NOT_EQUAL:
224
1.60k
    case QL_OP_GREATER_THAN:
225
1.60k
    case QL_OP_GREATER_THAN_EQUAL:
226
1.60k
    case QL_OP_LESS_THAN_EQUAL:
227
1.60k
    case QL_OP_LESS_THAN: {
228
0
      DCHECK(predicate.condition().operands(0).has_column_id()) <<
229
0
        "Complex expressions not supported in index predicate";
230
0
      DCHECK(predicate.condition().operands(1).has_value()) <<
231
0
        "Complex expressions not supported in index predicate";
232
1.60k
      if (VERIFY_RESULT(exprMatchesColOp(col_op, predicate))) return true;
233
878
      return false;
234
878
    }
235
0
    default:
236
0
      RSTATUS_DCHECK(false, InternalError, "Complex expressions not supported in index predicate. "
237
1.94k
        "Should have been blocked in index create itself");
238
1.94k
  }
239
1.94k
}
240
241
}  // namespace ql
242
}  // namespace yb