YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

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