/Users/deen/code/yugabyte-db/src/yb/bfql/bfql.h
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 | | // This module defines the entries to the builtin library functions. |
16 | | // FindQLOpcode() - Compiling builtin call into opcode. |
17 | | // ExecQLOpcode() - Execute builtin call using opcode. |
18 | | // |
19 | | // NOTES ON BUILTIN DEFINITIONS |
20 | | // ---------------------------- |
21 | | // Here's how new builtin functions are implemented or added to this library. |
22 | | // * Define C++ function (e.g. Token) in this library or any C++ library (yb_bfql). |
23 | | // If the function is defined in a different library, link it to this library. |
24 | | // * Define associated QL function ("token") by adding it to QL parser. |
25 | | // * In file "directory.cc", add an entry at the end of the kBFDirectory table. |
26 | | // { C++ func_name , QL func_name , return_type, { formal_parameter_types } } |
27 | | // Example: |
28 | | // { "Token", "token", STRING, {TYPEARGS} } |
29 | | // * The rest of the code would be auto-generated. |
30 | | // |
31 | | // NOTES ON PROCESSING BUILTIN CALLS |
32 | | // --------------------------------- |
33 | | // Here's how builtin calls can be processed using this library. |
34 | | // - Getting the opcode from a process (such as client or proxy server). |
35 | | // FindQLOpcode(function_name, // Input: Such as "+" |
36 | | // actual_parameter_types, // Input: Types of arguments. |
37 | | // opcode, // Output: Found associated opcode. |
38 | | // formal_parameter_types, // Output: Types of formal parameters. |
39 | | // return_type) // Input/Output: Return type. |
40 | | // - Send the opcode & parameter values to any processes (such as tablet server). |
41 | | // - The receiving process can then execute it. |
42 | | // ExecQLOpcode(opcode, // Input: Opcode from compilation. |
43 | | // param_values, // Input: Arguments. |
44 | | // return_value) // Output: Computed result. |
45 | | // |
46 | | // NOTES ON COMPILING BUILTIN CALLS |
47 | | // -------------------------------- |
48 | | // FindQLOpcode() should be called to type check a builtin calls. |
49 | | // * FindQLOpcode() does type checking for the parameters (function signature). |
50 | | // * FindQLOpcode() outputs a BFOpcode to be used at execution time. |
51 | | // * FindQLOpcode() outputs the formal parameter types. |
52 | | // - This is the signature of the function. |
53 | | // - The arguments to a builtin call must be converted to these exact formal types. |
54 | | // * FindQLOpcode() inputs / outputs the return type. |
55 | | // - If return type is not given (UNKNOWN_DATA), it returns the expected return type to caller. |
56 | | // - If return type is given, it checks if the type is compatible with the function's return type. |
57 | | // |
58 | | // NOTES ON EXECUTING BUILTIN CALLS |
59 | | // -------------------------------- |
60 | | // Call ExecQLFunc(opcode, args, result) |
61 | | // * Input arguments must be of exact datatypes as the formal parameter types. Operator "cast" |
62 | | // should have been used to convert these arguments when needed. |
63 | | // |
64 | | // * The return result must also be of expected type. The return value of expected type would be |
65 | | // written to this result. |
66 | | // |
67 | | // EXAMPPLE |
68 | | // -------- |
69 | | // The test "/yb/util/bfql/bfql-test.cc" would be a good example on builtin-call usage. |
70 | | // The file "/yb/yql/cql/ql/ptree/pt_bfunc.cc" can be used as example at the moment. |
71 | | //-------------------------------------------------------------------------------------------------- |
72 | | |
73 | | #ifndef YB_BFQL_BFQL_H_ |
74 | | #define YB_BFQL_BFQL_H_ |
75 | | |
76 | | #include <vector> |
77 | | #include <list> |
78 | | |
79 | | #include "yb/util/logging.h" |
80 | | #include "yb/bfql/bfql_template.h" |
81 | | #include "yb/bfql/tserver_opcodes.h" |
82 | | #include "yb/bfql/bfunc_names.h" |
83 | | |
84 | | namespace yb { |
85 | | namespace bfql { |
86 | | |
87 | | //-------------------------------------------------------------------------------------------------- |
88 | | // class BFCompileApi<PType, RType> has one main entry function - FindQLOpcode(). |
89 | | // |
90 | | // FindQLOpcode() finds the builtin opcode, signature, and return type using the function name. |
91 | | // - This function is a template function which accept with any parameter and result classes that |
92 | | // implement the following functions. |
93 | | // DataType ql_type_id(); |
94 | | // void set_ql_type_id(DataType); |
95 | | // |
96 | | // - Example |
97 | | // . QL expression treenode can be used for both PType and RType because it has the two required |
98 | | // functions for set & get ql_type_ids. |
99 | | // . Pseudo code. |
100 | | // using BFCompileQL = BFCompileApi<std::shared_ptr<QLValue>, QLValue>. |
101 | | // |
102 | | // - Example for a class that can be used as PType and Rtype for this template. |
103 | | // class YourArg { |
104 | | // DataType ql_type_id() { return ql_type_id_; } |
105 | | // void set_ql_type_id(DataType t) { ql_type_id_ = t; } |
106 | | // }; |
107 | | // |
108 | | // Builtin library provides interface for both raw and shared pointers. |
109 | | // vector<YourArgPointer> params = { ... }; |
110 | | // BFCompileApi<YourArg, YourArg>::FindQLOpcode(name, params, opcode, decl, result_ptr); |
111 | | |
112 | | template<typename PType, typename RType> |
113 | | class BFCompileApi { |
114 | | public: |
115 | | //------------------------------------------------------------------------------------------------ |
116 | | // Because we using Arena allocator, our compiler don't use standard collection types such as |
117 | | // std::vector. The following templates allow the compiler to resolve builtin calls with with |
118 | | // various collection types. |
119 | | |
120 | | // Interface for any collections of shared_ptrs. |
121 | | template<template<typename, typename> class CType, typename AType> |
122 | | static Status FindQLOpcodeImpl(const string& ql_name, |
123 | | const CType<std::shared_ptr<PType>, AType>& param_types, |
124 | | BFOpcode *opcode, |
125 | | const BFDecl **bfdecl, |
126 | 1.35k | const std::shared_ptr<RType>& result) { |
127 | 1.35k | Status s = FindOpcode<CType<std::shared_ptr<PType>, AType>, const std::shared_ptr<RType>&>( |
128 | 1.35k | ql_name, param_types, opcode, bfdecl, result); |
129 | 1.35k | VLOG(3) << "Compiled function call " << ql_name << ". Status: " << s.ToString()0 ; |
130 | 1.35k | return s; |
131 | 1.35k | } |
132 | | |
133 | | // Interface for any collections of raw pointers. |
134 | | template<template<typename, typename> class CType, typename AType> |
135 | | static Status FindQLOpcodeImpl(const string& ql_name, |
136 | | const CType<PType*, AType>& param_types, |
137 | | BFOpcode *opcode, |
138 | | const BFDecl **bfdecl, |
139 | | const std::shared_ptr<RType>& result) { |
140 | | Status s = FindOpcode<CType<PType*, AType>, const std::shared_ptr<RType>&>( |
141 | | ql_name, param_types, opcode, bfdecl, result); |
142 | | VLOG(3) << "Compiled function call " << ql_name << ". Status: " << s.ToString(); |
143 | | return s; |
144 | | } |
145 | | |
146 | | //------------------------------------------------------------------------------------------------ |
147 | | // Seeks builtin opcode using the given the std::vector of shared pointers. |
148 | | static Status FindQLOpcode(const string& ql_name, |
149 | | const std::vector<std::shared_ptr<PType>>& param_types, |
150 | | BFOpcode *opcode, |
151 | | const BFDecl **bfdecl, |
152 | 1.35k | const std::shared_ptr<RType>& result) { |
153 | 1.35k | return FindQLOpcodeImpl<std::vector>(ql_name, param_types, opcode, bfdecl, result); |
154 | 1.35k | } |
155 | | |
156 | | // Seeks builtin opcode using the given the std::vector of raw pointers. |
157 | | static Status FindQLOpcode(const string& ql_name, |
158 | | const std::vector<PType*>& param_types, |
159 | | BFOpcode *opcode, |
160 | | const BFDecl **bfdecl, |
161 | | const std::shared_ptr<RType>& result) { |
162 | | return FindQLOpcodeImpl<std::vector>(ql_name, param_types, opcode, bfdecl, result); |
163 | | } |
164 | | |
165 | | // Seeks builtin opcode using the given the std::vector of Datatypes. |
166 | | static Status FindQLOpcode(const string& ql_name, |
167 | | const std::vector<DataType>& actual_types, |
168 | | BFOpcode *opcode, |
169 | | const BFDecl **bfdecl, |
170 | 106 | DataType *return_type) { |
171 | 106 | Status s = FindOpcodeByType(ql_name, actual_types, opcode, bfdecl, return_type); |
172 | 106 | VLOG(3) << "Compiled function call " << ql_name << ". Status: " << s.ToString()0 ; |
173 | 106 | return s; |
174 | 106 | } |
175 | | |
176 | | // Seeks CAST opcode from one type to another. |
177 | 649 | static Status FindCastOpcode(DataType source, DataType target, BFOpcode *opcode) { |
178 | 649 | if (source == target || |
179 | 649 | source == DataType::NULL_VALUE_TYPE399 || |
180 | 649 | target == DataType::NULL_VALUE_TYPE399 ) { |
181 | 543 | *opcode = yb::bfql::BFOPCODE_NOOP; |
182 | 543 | return Status::OK(); |
183 | 543 | } |
184 | | |
185 | | // Find conversion opcode. |
186 | 106 | const BFDecl *found_decl = nullptr; |
187 | 106 | std::vector<DataType> actual_types = { source, target }; |
188 | 106 | DataType return_type = DataType::UNKNOWN_DATA; |
189 | 106 | return FindQLOpcode(bfql::kCastFuncName, actual_types, opcode, &found_decl, &return_type); |
190 | 649 | } |
191 | | }; |
192 | | |
193 | | //-------------------------------------------------------------------------------------------------- |
194 | | // class BFExecApi<PType, RType> has one main entry function - ExecQLOpcode(). |
195 | | // ExecQLOpcode() executes builtin calls with the given opcode and arguments and writes result |
196 | | // to the output parameter result. It reports error by returning Status. |
197 | | // |
198 | | // NOTES: |
199 | | // The API are using templates which have the following requirements. |
200 | | // |
201 | | // - The datatype of parameters must have the following member functions. |
202 | | // bool IsNull() const; |
203 | | // InternalType type() const; |
204 | | // int8_t int8_value() const; |
205 | | // int16_t int16_value() const; |
206 | | // int32_t int32_value() const; |
207 | | // int64_t int64_value() const; |
208 | | // float float_value() const; |
209 | | // double double_value() const; |
210 | | // bool bool_value() const; |
211 | | // const std::string& string_value() const; |
212 | | // Timestamp timestamp_value() const; |
213 | | // const std::string& binary_value() const; |
214 | | // InetAddress inetaddress_value() const; |
215 | | // |
216 | | // - The datatype of return-results must have the following member functions. |
217 | | // void SetNull(); |
218 | | // InternalType type() const; |
219 | | // void set_int8_value(int8_t val); |
220 | | // void set_int16_value(int16_t val); |
221 | | // void set_int32_value(int32_t val); |
222 | | // void set_int64_value(int64_t val); |
223 | | // void set_float_value(float val); |
224 | | // void set_double_value(double val); |
225 | | // void set_bool_value(bool val); |
226 | | // void set_string_value(const std::string& val); |
227 | | // void set_string_value(const char* val); |
228 | | // void set_string_value(const char* val, size_t size); |
229 | | // void set_timestamp_value(const Timestamp& val); |
230 | | // void set_timestamp_value(int64_t val); |
231 | | // void set_binary_value(const std::string& val); |
232 | | // void set_binary_value(const void* val, size_t size); |
233 | | // void set_inetaddress_value(const InetAddress& val); |
234 | | // |
235 | | // - Builtin-calls don't do implicit data conversion. They expect parameters to have the expected |
236 | | // type, and they always return data of the expected type. Arguments must be converted to correct |
237 | | // types before passing by using "cast" operator. |
238 | | template<typename PType, |
239 | | typename RType, |
240 | | template<typename, typename> class CType = std::vector, |
241 | | template<typename> class AType = std::allocator> |
242 | | class BFExecApi { |
243 | | public: |
244 | | // Declare table of function pointers that take shared_ptr as arguments and returned-result. |
245 | | static const vector<std::function<Status(const std::vector<std::shared_ptr<PType>>&, |
246 | | const std::shared_ptr<RType>&)>> |
247 | | kBFExecFuncs; |
248 | | |
249 | | // Declare table of function pointers that take raw pointers as arguments and returned-result. |
250 | | static const vector<std::function<Status(const std::vector<PType*>&, RType*)>> |
251 | | kBFExecFuncsRaw; |
252 | | |
253 | | // Declare table of function pointers that take ref as arguments and raw pointers as result |
254 | | static const vector<std::function<Status(std::vector<PType>*, RType*)>> |
255 | | kBFExecFuncsRefAndRaw; |
256 | | |
257 | | //------------------------------------------------------------------------------------------------ |
258 | | // Runs the associated entry in the table of function pointers on the given shared_ptrs. |
259 | | // kBFExecFuncs[opcode](args) |
260 | | static Status ExecQLOpcode(BFOpcode opcode, |
261 | | const std::vector<std::shared_ptr<PType>>& params, |
262 | 0 | const std::shared_ptr<RType>& result) { |
263 | | // TODO(neil) There has to be some sanity error check here. |
264 | 0 | RETURN_NOT_OK(CheckError(opcode, params, result)); |
265 | 0 | Status s = kBFExecFuncs[static_cast<int>(opcode)](params, result); |
266 | 0 | VLOG(3) << "Executed builtin call(" << int(opcode) << "). Status: " << s.ToString(); |
267 | 0 | return s; |
268 | 0 | } |
269 | | |
270 | | // TODO(neil) Because opcodes are created and executed by different processes, some sanity error |
271 | | // checking must be done at run time in logging mode. |
272 | | static Status CheckError(BFOpcode opcode, |
273 | | const std::vector<std::shared_ptr<PType>>& params, |
274 | 0 | const std::shared_ptr<RType>& result) { |
275 | | // TODO(neil) Currently, the execution phase is not yet implemented, so it'd be immature to |
276 | | // code for error-check here. Once it is implemented, we'll know what to check. |
277 | 0 | if (VLOG_IS_ON(3)) { |
278 | 0 | LOG(INFO) << "Executing opcode " << int(opcode); |
279 | 0 | } |
280 | 0 | return Status::OK(); |
281 | 0 | } |
282 | | |
283 | | //------------------------------------------------------------------------------------------------ |
284 | | // Runs the associated entry in the table of function pointers on the given raw pointers. |
285 | | // kBFExecFuncsRaw[opcode](args) |
286 | | static Status ExecQLOpcode(BFOpcode opcode, |
287 | | const std::vector<PType*>& params, |
288 | 0 | RType *result) { |
289 | | // TODO(neil) There has to be some sanity error check here. |
290 | 0 | RETURN_NOT_OK(CheckError(opcode, params, result)); |
291 | 0 | Status s = kBFExecFuncsRaw[static_cast<int>(opcode)](params, result); |
292 | 0 | VLOG(3) << "Executed builtin call(" << int(opcode) << "). Status: " << s.ToString(); |
293 | 0 | return s; |
294 | 0 | } |
295 | | |
296 | | static Status CheckError(BFOpcode opcode, |
297 | | const std::vector<PType*>& params, |
298 | 0 | RType *result) { |
299 | | // TODO(neil) Currently, the execution phase is not yet implemented, so it'd be immature to |
300 | | // code for error-check here. Once it is implemented, we'll know what to check. |
301 | 0 | if (VLOG_IS_ON(3)) { |
302 | 0 | LOG(INFO) << "Executing opcode " << int(opcode); |
303 | 0 | } |
304 | 0 | return Status::OK(); |
305 | 0 | } |
306 | | |
307 | | //------------------------------------------------------------------------------------------------ |
308 | | // Runs the associated entry in the table of function pointers on the given raw pointers. |
309 | | // kBFExecFuncsRefAndRaw[opcode](args) |
310 | | static Status ExecQLOpcode(BFOpcode opcode, |
311 | | std::vector<PType> *params, |
312 | 724 | RType *result) { |
313 | | // TODO(neil) There has to be some sanity error check here. |
314 | 724 | RETURN_NOT_OK(CheckError(opcode, params, result)); |
315 | 724 | Status s = kBFExecFuncsRefAndRaw[static_cast<int>(opcode)](params, result); |
316 | 724 | VLOG(3) << "Executed builtin call(" << int(opcode) << "). Status: " << s.ToString()0 ; |
317 | 724 | return s; |
318 | 724 | } |
319 | | |
320 | | static Status CheckError(BFOpcode opcode, |
321 | | std::vector<PType> *params, |
322 | 724 | RType *result) { |
323 | | // TODO(neil) Currently, the execution phase is not yet implemented, so it'd be immature to |
324 | | // code for error-check here. Once it is implemented, we'll know what to check. |
325 | 724 | if (VLOG_IS_ON(3)) { |
326 | 0 | LOG(INFO) << "Executing opcode " << int(opcode); |
327 | 0 | } |
328 | 724 | return Status::OK(); |
329 | 724 | } |
330 | | }; |
331 | | |
332 | | //-------------------------------------------------------------------------------------------------- |
333 | | // This class is conveniently and ONLY for testing purpose. It executes builtin calls by names |
334 | | // instead of opcode. |
335 | | template<typename PType, |
336 | | typename RType, |
337 | | template<typename, typename> class CType = std::vector, |
338 | | template<typename> class AType = std::allocator> |
339 | | class BFExecImmediateApi : public BFExecApi<PType, RType, CType, AType> { |
340 | | public: |
341 | | // Interface for shared_ptr. |
342 | | static Status ExecQLFunc(const string& ql_name, |
343 | | const std::vector<std::shared_ptr<PType>>& params, |
344 | | const std::shared_ptr<RType>& result) { |
345 | | BFOpcode opcode; |
346 | | const BFDecl *bfdecl; |
347 | | RETURN_NOT_OK((FindOpcode<std::vector<std::shared_ptr<PType>>, const std::shared_ptr<RType>&>( |
348 | | ql_name, params, &opcode, &bfdecl, result))); |
349 | | return BFExecApi<PType, RType>::ExecQLOpcode(opcode, params, result); |
350 | | } |
351 | | |
352 | | // Interface for raw pointer. |
353 | | static Status ExecQLFunc(const string& ql_name, |
354 | | const std::vector<PType*>& params, |
355 | | RType *result) { |
356 | | BFOpcode opcode; |
357 | | const BFDecl *bfdecl; |
358 | | RETURN_NOT_OK( |
359 | | (FindOpcode<std::vector<PType*>, RType*>(ql_name, params, &opcode, &bfdecl, result))); |
360 | | return BFExecApi<PType, RType>::ExecQLOpcode(opcode, params, result); |
361 | | } |
362 | | }; |
363 | | |
364 | | } // namespace bfql |
365 | | } // namespace yb |
366 | | |
367 | | //-------------------------------------------------------------------------------------------------- |
368 | | // Generated tables "kBFExecFuncs" and "kBFExecFuncsRaw". |
369 | | // Because the tables must be initialized after the specification for "class BFExecApi", we have to |
370 | | // include header file "gen_bfunc_table.h" at the end of this file. |
371 | | #include "yb/bfql/gen_bfunc_table.h" |
372 | | |
373 | | #endif // YB_BFQL_BFQL_H_ |