/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 | 334 | bool IsAggregateOpcode(TSOpcode op) { |
31 | 334 | switch (op) { |
32 | 20 | case TSOpcode::kAvg: FALLTHROUGH_INTENDED; |
33 | 90 | case TSOpcode::kCount: FALLTHROUGH_INTENDED; |
34 | 118 | case TSOpcode::kMax: FALLTHROUGH_INTENDED; |
35 | 147 | case TSOpcode::kMin: FALLTHROUGH_INTENDED; |
36 | 175 | case TSOpcode::kSum: |
37 | 175 | return true; |
38 | 159 | default: |
39 | 159 | return false; |
40 | 334 | } |
41 | 334 | } |
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.50k | const std::vector<DataType>& actual_types) { |
57 | | // Check parameter count. |
58 | 6.50k | const auto formal_count = signature.size(); |
59 | 6.50k | const auto actual_count = actual_types.size(); |
60 | | |
61 | | // Check for exact match. |
62 | 6.50k | size_t index; |
63 | 9.24k | for (index = 0; index < formal_count; index++) { |
64 | | // Check if the signature accept varargs which can be matched with the rest of arguments. |
65 | 8.41k | if (signature[index] == DataType::TYPEARGS) { |
66 | 127 | return true; |
67 | 127 | } |
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.29k | if (index >= actual_count || (signature[index] != actual_types[index] && |
74 | 6.99k | signature[index] != DataType::NULL_VALUE_TYPE)) { |
75 | 5.55k | return false; |
76 | 5.55k | } |
77 | 8.29k | } |
78 | | |
79 | | // Assert that the number of arguments is the same as the formal count. |
80 | 828 | if (index < actual_count) { |
81 | 0 | return false; |
82 | 0 | } |
83 | 828 | return true; |
84 | 828 | } |
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++) { |
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])) { |
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 | 135 | } |
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++) { |
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])) { |
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 | 47 | } |
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.61k | DataType *return_type) { |
183 | | |
184 | | // Find a compatible operator, and raise error if there's more than one match. |
185 | 1.61k | const BFOperator *compatible_operator = nullptr; |
186 | 7.33k | while (true) { |
187 | 7.33k | const BFOperator *bf_operator = kBFOperators[static_cast<int>(max_opcode)].get(); |
188 | 7.33k | DCHECK(max_opcode == bf_operator->opcode()); |
189 | | |
190 | | // Check if each parameter has compatible type match. |
191 | 7.33k | if (compare_signature(bf_operator->param_types(), actual_types)) { |
192 | | // Found a compatible match. Make sure that it is the only match. |
193 | 1.13k | 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.13k | compatible_operator = bf_operator; |
198 | 0 | VLOG(3) << "Matched function with opcode " << static_cast<int>(max_opcode); |
199 | 1.13k | } |
200 | | |
201 | | // Break the loop if we have processed all operators in the overloading chain. |
202 | 7.33k | if (max_opcode == bf_operator->overloaded_opcode()) { |
203 | 1.61k | break; |
204 | 1.61k | } |
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.61k | 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.13k | if (return_type != nullptr) { |
218 | 1.13k | if (QLType::IsUnknown(*return_type)) { |
219 | 1.05k | *return_type = compatible_operator->return_type(); |
220 | 81 | } else if (!IsCompatible(*return_type, compatible_operator->return_type())) { |
221 | 38 | return STATUS(InvalidArgument, |
222 | 38 | Substitute("Return-type mismatch in call to builtin function '$0'", ql_name)); |
223 | 38 | } |
224 | 1.09k | } |
225 | | |
226 | | // Raise error if the function execution was not yet implemented. |
227 | 1.09k | 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.09k | *found_opcode = compatible_operator->opcode(); |
233 | 1.09k | *found_decl = compatible_operator->op_decl(); |
234 | 1.09k | return Status::OK(); |
235 | 1.09k | } |
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.26k | DataType *return_type) { |
249 | 1.26k | auto entry = kBfqlName2Opcode.find(ql_name); |
250 | 1.26k | if (entry == kBfqlName2Opcode.end()) { |
251 | 0 | VLOG(3) << strings::Substitute("Function '$0' does not exist", ql_name); |
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.24k | Status s = FindMatch(ql_name, HasExactTypeSignature, entry->second, actual_types, |
273 | 1.24k | opcode, bfdecl, return_type); |
274 | 0 | VLOG(3) << "Seek exact match for function call " << ql_name << "(): " << s.ToString(); |
275 | | |
276 | 1.24k | if (ql_name != kCastFuncName && s.IsNotFound()) { |
277 | 254 | s = FindMatch(ql_name, HasSimilarTypeSignature, entry->second, actual_types, |
278 | 254 | opcode, bfdecl, return_type); |
279 | 0 | VLOG(3) << "Seek similar match for function call " << ql_name << "(): " << s.ToString(); |
280 | | |
281 | 254 | if (s.IsNotFound()) { |
282 | 119 | s = FindMatch(ql_name, HasCompatibleTypeSignature, entry->second, actual_types, |
283 | 119 | opcode, bfdecl, return_type); |
284 | 0 | VLOG(3) << "Seek compatible match for function call " << ql_name << "(): " << s.ToString(); |
285 | 119 | } |
286 | 254 | } |
287 | | |
288 | 1.24k | return s; |
289 | 1.24k | } |
290 | | |
291 | | } // namespace bfql |
292 | | } // namespace yb |