YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/bfql/bfql.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/bfql/bfql.h"
17
18
#include <functional>
19
#include <unordered_map>
20
21
using std::function;
22
using std::vector;
23
using std::unordered_map;
24
using strings::Substitute;
25
26
namespace yb {
27
namespace bfql {
28
29
//--------------------------------------------------------------------------------------------------
30
344
bool IsAggregateOpcode(TSOpcode op) {
31
344
  switch (op) {
32
20
  case TSOpcode::kAvg: FALLTHROUGH_INTENDED;
33
99
  case TSOpcode::kCount: FALLTHROUGH_INTENDED;
34
127
  case TSOpcode::kMax: FALLTHROUGH_INTENDED;
35
156
  case TSOpcode::kMin: FALLTHROUGH_INTENDED;
36
184
  case TSOpcode::kSum:
37
184
    return true;
38
160
  default:
39
160
    return false;
40
344
  }
41
344
}
42
43
//--------------------------------------------------------------------------------------------------
44
// Check compatible type in function call.
45
380
inline bool IsCompatible(DataType left, DataType right) {
46
380
  return QLType::IsPotentiallyConvertible(left, right);
47
380
}
48
49
//--------------------------------------------------------------------------------------------------
50
// HasExactSignature() is a predicate to check if the datatypes of actual parameters (arguments)
51
// and formal parameters (signature) are identical.
52
// NOTES:
53
//   PTypePtr can be a (shared_ptr<MyClass>) or a raw pointer (MyClass*)
54
55
static bool HasExactTypeSignature(const std::vector<DataType>& signature,
56
6.79k
                                  const std::vector<DataType>& actual_types) {
57
  // Check parameter count.
58
6.79k
  const auto formal_count = signature.size();
59
6.79k
  const auto actual_count = actual_types.size();
60
61
  // Check for exact match.
62
6.79k
  size_t index;
63
9.54k
  for (index = 0; index < formal_count; 
index++2.74k
) {
64
    // Check if the signature accept varargs which can be matched with the rest of arguments.
65
8.70k
    if (signature[index] == DataType::TYPEARGS) {
66
407
      return true;
67
407
    }
68
69
    // Return false if one of the following is true.
70
    // - The number of arguments is less than the formal count.
71
    // - The datatype of an argument is not an exact match of the signature type and the
72
    //   signature type is not ANYTYPE.
73
8.30k
    if (index >= actual_count || 
(8.16k
signature[index] != actual_types[index]8.16k
&&
74
8.16k
        
signature[index] != DataType::NULL_VALUE_TYPE6.99k
)) {
75
5.55k
      return false;
76
5.55k
    }
77
8.30k
  }
78
79
  // Assert that the number of arguments is the same as the formal count.
80
838
  if (index < actual_count) {
81
0
    return false;
82
0
  }
83
838
  return true;
84
838
}
85
86
//--------------------------------------------------------------------------------------------------
87
// HasSimilarSignature() is a predicate to check if the datatypes of actual parameters (arguments)
88
// and formal parameters (signature) are similar.
89
//
90
// Similar is mainly used for integers vs floating point values.
91
//   INT8 is "similar" to INT64.
92
//   INT8 is NOT "similar" to DOUBLE.
93
//   FLOAT is "similar" to DOUBLE.
94
// This rule is to help resolve the overloading functions between integers and float point data.
95
//
96
// NOTES:
97
//   PTypePtr and RTypePtr can be either a (shared_ptr<MyClass>) or a raw pointer (MyClass*)
98
99
static bool HasSimilarTypeSignature(const std::vector<DataType>& signature,
100
557
                                    const std::vector<DataType>& actual_types) {
101
557
  const auto formal_count = signature.size();
102
557
  const auto actual_count = actual_types.size();
103
104
  // Check for exact match.
105
557
  size_t index;
106
783
  for (index = 0; index < formal_count; 
index++226
) {
107
    // Check if the signature accept varargs which can be matched with the rest of arguments.
108
647
    if (signature[index] == DataType::TYPEARGS) {
109
0
      return true;
110
0
    }
111
112
    // Return false if one of the following is true.
113
    // - The number of arguments is less than the formal count.
114
    // - The datatype of an argument is not an similar match of the signature type.
115
647
    if (index >= actual_count || 
!QLType::IsSimilar(signature[index], actual_types[index])640
) {
116
421
      return false;
117
421
    }
118
647
  }
119
120
  // Assert that the number of arguments is the same as the formal count.
121
136
  if (index < actual_count) {
122
1
    return false;
123
1
  }
124
125
135
  return true;
126
136
}
127
128
//--------------------------------------------------------------------------------------------------
129
// HasCompatibleSignature() is a predicate to check if the arguments is convertible to the
130
// signature.
131
// Examples:
132
// - INT16 is convertible to DOUBLE, so passing an int16 value to func(DOUBLE) is valid.
133
// - In CQL, DOUBLE is not convertible to INT16, so passing a double value to func(INT26) is
134
// invalid. This case would become valid if YugaByte eases this conversion restriction.
135
//
136
// NOTES:
137
//   PTypePtr and RTypePtr can be either a (shared_ptr<MyClass>) or a raw pointer (MyClass*).
138
139
static bool HasCompatibleTypeSignature(const std::vector<DataType>& signature,
140
269
                                       const std::vector<DataType>& actual_types) {
141
142
269
  const auto formal_count = signature.size();
143
269
  const auto actual_count = actual_types.size();
144
145
  // Check for compatible match.
146
269
  size_t index;
147
483
  for (index = 0; index < formal_count; 
index++214
) {
148
    // Check if the signature accept varargs which can be matched with the rest of arguments.
149
434
    if (signature[index] == DataType::TYPEARGS) {
150
0
      return true;
151
0
    }
152
153
    // Return false if one of the following is true.
154
    // - The number of arguments is less than the formal count.
155
    // - The datatype of an argument is not a compatible match of the signature type.
156
434
    if (index >= actual_count || 
!IsCompatible(signature[index], actual_types[index])299
) {
157
220
      return false;
158
220
    }
159
434
  }
160
161
  // Assert that the number of arguments is the same as the formal count.
162
49
  if (index < actual_count) {
163
2
    return false;
164
2
  }
165
166
47
  return true;
167
49
}
168
169
//--------------------------------------------------------------------------------------------------
170
// Searches all overloading versions of a function and finds exactly one function specification
171
// whose signature matches with the datatypes of the arguments.
172
// NOTES:
173
//   "compare_signature" is a predicate to compare datatypes of formal and actual parameters.
174
//   PTypePtr and RTypePtr can be either a (shared_ptr<MyClass>) or a raw pointer (MyClass*)
175
static Status FindMatch(
176
    const string& ql_name,
177
    function<bool(const std::vector<DataType>&, const std::vector<DataType>&)> compare_signature,
178
    BFOpcode max_opcode,
179
    const std::vector<DataType>& actual_types,
180
    BFOpcode *found_opcode,
181
    const BFDecl **found_decl,
182
1.90k
    DataType *return_type) {
183
184
  // Find a compatible operator, and raise error if there's more than one match.
185
1.90k
  const BFOperator *compatible_operator = nullptr;
186
7.62k
  while (true) {
187
7.62k
    const BFOperator *bf_operator = kBFOperators[static_cast<int>(max_opcode)].get();
188
7.62k
    DCHECK(max_opcode == bf_operator->opcode());
189
190
    // Check if each parameter has compatible type match.
191
7.62k
    if (compare_signature(bf_operator->param_types(), actual_types)) {
192
      // Found a compatible match. Make sure that it is the only match.
193
1.42k
      if (compatible_operator != nullptr) {
194
0
        return STATUS(InvalidArgument,
195
0
                      Substitute("Found too many matches for builtin function '$0'", ql_name));
196
0
      }
197
1.42k
      compatible_operator = bf_operator;
198
1.42k
      VLOG
(3) << "Matched function with opcode " << static_cast<int>(max_opcode)0
;
199
1.42k
    }
200
201
    // Break the loop if we have processed all operators in the overloading chain.
202
7.62k
    if (max_opcode == bf_operator->overloaded_opcode()) {
203
1.90k
      break;
204
1.90k
    }
205
206
    // Jump to the next overloading opcode.
207
5.71k
    max_opcode = bf_operator->overloaded_opcode();
208
5.71k
  }
209
210
  // Returns error if no match is found.
211
1.90k
  if (compatible_operator == nullptr) {
212
479
    return STATUS(NotFound,
213
479
                  Substitute("Signature mismatch in call to builtin function '$0'", ql_name));
214
479
  }
215
216
  // Returns error if the return type is not compatible.
217
1.42k
  if (return_type != nullptr) {
218
1.42k
    if (QLType::IsUnknown(*return_type)) {
219
1.34k
      *return_type = compatible_operator->return_type();
220
1.34k
    } else 
if (81
!IsCompatible(*return_type, compatible_operator->return_type())81
) {
221
38
      return STATUS(InvalidArgument,
222
38
                    Substitute("Return-type mismatch in call to builtin function '$0'", ql_name));
223
38
    }
224
1.42k
  }
225
226
  // Raise error if the function execution was not yet implemented.
227
1.38k
  if (!compatible_operator->op_decl()->implemented()) {
228
0
    return STATUS(NotSupported,
229
0
                  Substitute("Builtin function '$0' is not yet implemented", ql_name));
230
0
  }
231
232
1.38k
  *found_opcode = compatible_operator->opcode();
233
1.38k
  *found_decl = compatible_operator->op_decl();
234
1.38k
  return Status::OK();
235
1.38k
}
236
237
//--------------------------------------------------------------------------------------------------
238
// Find the builtin opcode, declaration, and return type for a builtin call.
239
// Inputs: Builtin function name and parameter types.
240
// Outputs: opcode and bfdecl.
241
// In/Out parameter: return_type
242
//   If return_type is given, check if it is compatible with the declaration.
243
//   If not, return_type is an output parameter whose value is the return type of the builtin.
244
Status FindOpcodeByType(const string& ql_name,
245
                        const std::vector<DataType>& actual_types,
246
                        BFOpcode *opcode,
247
                        const BFDecl **bfdecl,
248
1.55k
                        DataType *return_type) {
249
1.55k
  auto entry = kBfqlName2Opcode.find(ql_name);
250
1.55k
  if (entry == kBfqlName2Opcode.end()) {
251
19
    VLOG
(3) << strings::Substitute("Function '$0' does not exist", ql_name)0
;
252
19
    return STATUS(NotFound, strings::Substitute("Function '$0' does not exist", ql_name));
253
19
  }
254
255
  // Seek the correct overload functions in the following order:
256
  // - Find the exact signature match.
257
  //   Example:
258
  //   . Overload #1: FuncX(int8_t i) would be used for the call FuncX(int8_t(7)).
259
  //   . Overload #2: FuncX(int16_t i) would be used for the call FuncX(int16_t(7)).
260
  //
261
  // - For "cast" operator, if exact match is not found, return error. For all other operators,
262
  //   continue to the next steps.
263
  //
264
  // - Find the similar signature match.
265
  //   Example:
266
  //   . Overload #2: FuncY(int64_t i) would be used for FuncY(int8_t(7)).
267
  //       int64_t and int8_t are both integer values.
268
  //   . Overload #1: FuncY(double d) would be used for FuncY(float(7)).
269
  //       double & float are both floating values.
270
  //
271
  // - Find the compatible match. Signatures are of convertible datatypes.
272
1.53k
  Status s = FindMatch(ql_name, HasExactTypeSignature, entry->second, actual_types,
273
1.53k
                       opcode, bfdecl, return_type);
274
1.53k
  VLOG
(3) << "Seek exact match for function call " << ql_name << "(): " << s.ToString()0
;
275
276
1.53k
  if (ql_name != kCastFuncName && 
s.IsNotFound()1.42k
) {
277
254
    s = FindMatch(ql_name, HasSimilarTypeSignature, entry->second, actual_types,
278
254
                  opcode, bfdecl, return_type);
279
254
    VLOG
(3) << "Seek similar match for function call " << ql_name << "(): " << s.ToString()0
;
280
281
254
    if (s.IsNotFound()) {
282
119
      s = FindMatch(ql_name, HasCompatibleTypeSignature, entry->second, actual_types,
283
119
                    opcode, bfdecl, return_type);
284
119
      VLOG
(3) << "Seek compatible match for function call " << ql_name << "(): " << s.ToString()0
;
285
119
    }
286
254
  }
287
288
1.53k
  return s;
289
1.55k
}
290
291
} // namespace bfql
292
} // namespace yb