/Users/deen/code/yugabyte-db/src/yb/bfpg/bfpg.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 | | // FindPgsqlOpcode() - Compiling builtin call into opcode. |
17 | | // ExecPgsqlOpcode() - 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_bfpg). |
23 | | // If the function is defined in a different library, link it to this library. |
24 | | // * Define associated PGSQL function ("token") by adding it to PGSQL parser. |
25 | | // * In file "directory.cc", add an entry at the end of the kBFDirectory table. |
26 | | // { C++ func_name , PGSQL 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 | | // FindPgsqlOpcode(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 | | // ExecPgsqlOpcode(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 | | // FindPgsqlOpcode() should be called to type check a builtin calls. |
49 | | // * FindPgsqlOpcode() does type checking for the parameters (function signature). |
50 | | // * FindPgsqlOpcode() outputs a BFOpcode to be used at execution time. |
51 | | // * FindPgsqlOpcode() 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 | | // * FindPgsqlOpcode() 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 ExecPgsqlFunc(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/bfpg/bfpg-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_BFPG_BFPG_H |
74 | | #define YB_BFPG_BFPG_H |
75 | | |
76 | | #include <vector> |
77 | | #include <list> |
78 | | |
79 | | #include "yb/util/logging.h" |
80 | | #include "yb/bfpg/bfpg_template.h" |
81 | | #include "yb/bfpg/tserver_opcodes.h" |
82 | | |
83 | | namespace yb { |
84 | | namespace bfpg { |
85 | | |
86 | | const char *const kCastFuncName = "cast"; |
87 | | |
88 | | //-------------------------------------------------------------------------------------------------- |
89 | | // class BFCompileApi<PType, RType> has one main entry function - FindPgsqlOpcode(). |
90 | | // |
91 | | // FindPgsqlOpcode() finds the builtin opcode, signature, and return type using the function name. |
92 | | // - This function is a template function which accept with any parameter and result classes that |
93 | | // implement the following functions. |
94 | | // DataType ql_type_id(); |
95 | | // void set_ql_type_id(DataType); |
96 | | // |
97 | | // - Example |
98 | | // . PGSQL expression treenode can be used for both PType and RType because it has the two |
99 | | // required functions for set & get ql_type_ids. |
100 | | // . Pseudo code. |
101 | | // using BFCompileQL = BFCompileApi<std::shared_ptr<QLValue>, QLValue>. |
102 | | // |
103 | | // - Example for a class that can be used as PType and Rtype for this template. |
104 | | // class YourArg { |
105 | | // DataType ql_type_id() { return ql_type_id_; } |
106 | | // void set_ql_type_id(DataType t) { ql_type_id_ = t; } |
107 | | // }; |
108 | | // |
109 | | // Builtin library provides interface for both raw and shared pointers. |
110 | | // vector<YourArgPointer> params = { ... }; |
111 | | // BFCompileApi<YourArg, YourArg>::FindPgsqlOpcode(name, params, opcode, decl, result_ptr); |
112 | | |
113 | | template<typename PType, typename RType> |
114 | | class BFCompileApi { |
115 | | public: |
116 | | //------------------------------------------------------------------------------------------------ |
117 | | // Because we using Arena allocator, our compiler don't use standard collection types such as |
118 | | // std::vector. The following templates allow the compiler to resolve builtin calls with with |
119 | | // various collection types. |
120 | | |
121 | | // Interface for any collections of shared_ptrs. |
122 | | template<template<typename, typename> class CType, typename AType> |
123 | | static Status FindPgsqlOpcodeImpl(const string& ql_name, |
124 | | const CType<std::shared_ptr<PType>, AType>& param_types, |
125 | | BFOpcode *opcode, |
126 | | const BFDecl **bfdecl, |
127 | 14 | const std::shared_ptr<RType>& result) { |
128 | 14 | Status s = FindOpcode<CType<std::shared_ptr<PType>, AType>, const std::shared_ptr<RType>&>( |
129 | 14 | ql_name, param_types, opcode, bfdecl, result); |
130 | 0 | VLOG(3) << "Compiled function call " << ql_name << ". Status: " << s.ToString(); |
131 | 14 | return s; |
132 | 14 | } |
133 | | |
134 | | // Interface for any collections of raw pointers. |
135 | | template<template<typename, typename> class CType, typename AType> |
136 | | static Status FindPgsqlOpcodeImpl(const string& ql_name, |
137 | | const CType<PType*, AType>& param_types, |
138 | | BFOpcode *opcode, |
139 | | const BFDecl **bfdecl, |
140 | | const std::shared_ptr<RType>& result) { |
141 | | Status s = FindOpcode<CType<PType*, AType>, const std::shared_ptr<RType>&>( |
142 | | ql_name, param_types, opcode, bfdecl, result); |
143 | | VLOG(3) << "Compiled function call " << ql_name << ". Status: " << s.ToString(); |
144 | | return s; |
145 | | } |
146 | | |
147 | | //------------------------------------------------------------------------------------------------ |
148 | | // Seeks builtin opcode using the given the std::vector of shared pointers. |
149 | | static Status FindPgsqlOpcode(const string& ql_name, |
150 | | const std::vector<std::shared_ptr<PType>>& param_types, |
151 | | BFOpcode *opcode, |
152 | | const BFDecl **bfdecl, |
153 | 14 | const std::shared_ptr<RType>& result) { |
154 | 14 | return FindPgsqlOpcodeImpl<std::vector>(ql_name, param_types, opcode, bfdecl, result); |
155 | 14 | } |
156 | | |
157 | | // Seeks builtin opcode using the given the std::vector of raw pointers. |
158 | | static Status FindPgsqlOpcode(const string& ql_name, |
159 | | const std::vector<PType*>& param_types, |
160 | | BFOpcode *opcode, |
161 | | const BFDecl **bfdecl, |
162 | | const std::shared_ptr<RType>& result) { |
163 | | return FindPgsqlOpcodeImpl<std::vector>(ql_name, param_types, opcode, bfdecl, result); |
164 | | } |
165 | | |
166 | | // Seeks builtin opcode using the given the std::vector of Datatypes. |
167 | | static Status FindPgsqlOpcode(const string& ql_name, |
168 | | const std::vector<DataType>& actual_types, |
169 | | BFOpcode *opcode, |
170 | | const BFDecl **bfdecl, |
171 | | DataType *return_type) { |
172 | | Status s = FindOpcodeByType(ql_name, actual_types, opcode, bfdecl, return_type); |
173 | | VLOG(3) << "Compiled function call " << ql_name << ". Status: " << s.ToString(); |
174 | | return s; |
175 | | } |
176 | | |
177 | | // Seeks CAST opcode from one type to another. |
178 | | static Status FindCastOpcode(DataType source, DataType target, BFOpcode *opcode) { |
179 | | if (source == target || |
180 | | source == DataType::NULL_VALUE_TYPE || |
181 | | target == DataType::NULL_VALUE_TYPE) { |
182 | | *opcode = yb::bfpg::BFOPCODE_NOOP; |
183 | | return Status::OK(); |
184 | | } |
185 | | |
186 | | // Find conversion opcode. |
187 | | const BFDecl *found_decl = nullptr; |
188 | | std::vector<DataType> actual_types = { source, target }; |
189 | | DataType return_type = DataType::UNKNOWN_DATA; |
190 | | return FindPgsqlOpcode(bfpg::kCastFuncName, actual_types, opcode, &found_decl, |
191 | | &return_type); |
192 | | } |
193 | | }; |
194 | | |
195 | | //-------------------------------------------------------------------------------------------------- |
196 | | // class BFExecApi<PType, RType> has one main entry function - ExecPgsqlOpcode(). |
197 | | // ExecPgsqlOpcode() executes builtin calls with the given opcode and arguments and writes result |
198 | | // to the output parameter result. It reports error by returning Status. |
199 | | // |
200 | | // NOTES: |
201 | | // The API are using templates which have the following requirements. |
202 | | // |
203 | | // - The datatype of parameters must have the following member functions. |
204 | | // bool IsNull() const; |
205 | | // InternalType type() const; |
206 | | // int8_t int8_value() const; |
207 | | // int16_t int16_value() const; |
208 | | // int32_t int32_value() const; |
209 | | // int64_t int64_value() const; |
210 | | // float float_value() const; |
211 | | // double double_value() const; |
212 | | // bool bool_value() const; |
213 | | // const std::string& string_value() const; |
214 | | // Timestamp timestamp_value() const; |
215 | | // const std::string& binary_value() const; |
216 | | // InetAddress inetaddress_value() const; |
217 | | // |
218 | | // - The datatype of return-results must have the following member functions. |
219 | | // void SetNull(); |
220 | | // InternalType type() const; |
221 | | // void set_int8_value(int8_t val); |
222 | | // void set_int16_value(int16_t val); |
223 | | // void set_int32_value(int32_t val); |
224 | | // void set_int64_value(int64_t val); |
225 | | // void set_float_value(float val); |
226 | | // void set_double_value(double val); |
227 | | // void set_bool_value(bool val); |
228 | | // void set_string_value(const std::string& val); |
229 | | // void set_string_value(const char* val); |
230 | | // void set_string_value(const char* val, size_t size); |
231 | | // void set_timestamp_value(const Timestamp& val); |
232 | | // void set_timestamp_value(int64_t val); |
233 | | // void set_binary_value(const std::string& val); |
234 | | // void set_binary_value(const void* val, size_t size); |
235 | | // void set_inetaddress_value(const InetAddress& val); |
236 | | // |
237 | | // - Builtin-calls don't do implicit data conversion. They expect parameters to have the expected |
238 | | // type, and they always return data of the expected type. Arguments must be converted to correct |
239 | | // types before passing by using "cast" operator. |
240 | | template<typename PType, |
241 | | typename RType, |
242 | | template<typename, typename> class CType = std::vector, |
243 | | template<typename> class AType = std::allocator> |
244 | | class BFExecApi { |
245 | | public: |
246 | | // Declare table of function pointers that take shared_ptr as arguments and returned-result. |
247 | | static const vector<std::function<Status(const std::vector<std::shared_ptr<PType>>&, |
248 | | const std::shared_ptr<RType>&)>> |
249 | | kBFExecFuncs; |
250 | | |
251 | | // Declare table of function pointers that take raw pointers as arguments and returned-result. |
252 | | static const vector<std::function<Status(const std::vector<PType*>&, RType*)>> |
253 | | kBFExecFuncsRaw; |
254 | | |
255 | | // Declare table of function pointers that take ref as arguments and raw pointers as result |
256 | | static const vector<std::function<Status(std::vector<PType>*, RType*)>> |
257 | | kBFExecFuncsRefAndRaw; |
258 | | |
259 | | //------------------------------------------------------------------------------------------------ |
260 | | // Runs the associated entry in the table of function pointers on the given shared_ptrs. |
261 | | // kBFExecFuncs[opcode](args) |
262 | | static Status ExecPgsqlOpcode(BFOpcode opcode, |
263 | | const std::vector<std::shared_ptr<PType>>& params, |
264 | 13 | const std::shared_ptr<RType>& result) { |
265 | | // TODO(neil) There has to be some sanity error check here. |
266 | 13 | RETURN_NOT_OK(CheckError(opcode, params, result)); |
267 | 13 | Status s = kBFExecFuncs[static_cast<int>(opcode)](params, result); |
268 | 0 | VLOG(3) << "Executed builtin call(" << int(opcode) << "). Status: " << s.ToString(); |
269 | 13 | return s; |
270 | 13 | } _ZN2yb4bfpg9BFExecApiINS0_11BFTestValueES2_NSt3__16vectorENS3_9allocatorEE15ExecPgsqlOpcodeENS0_8BFOpcodeERKNS4_INS3_10shared_ptrIS2_EENS5_IS9_EEEERKS9_ Line | Count | Source | 264 | 13 | const std::shared_ptr<RType>& result) { | 265 | | // TODO(neil) There has to be some sanity error check here. | 266 | 13 | RETURN_NOT_OK(CheckError(opcode, params, result)); | 267 | 13 | Status s = kBFExecFuncs[static_cast<int>(opcode)](params, result); | 268 | 0 | VLOG(3) << "Executed builtin call(" << int(opcode) << "). Status: " << s.ToString(); | 269 | 13 | return s; | 270 | 13 | } |
Unexecuted instantiation: _ZN2yb4bfpg9BFExecApiINS_7QLValueES2_NSt3__16vectorENS3_9allocatorEE15ExecPgsqlOpcodeENS0_8BFOpcodeERKNS4_INS3_10shared_ptrIS2_EENS5_IS9_EEEERKS9_ |
271 | | |
272 | | // TODO(neil) Because opcodes are created and executed by different processes, some sanity error |
273 | | // checking must be done at run time in logging mode. |
274 | | static Status CheckError(BFOpcode opcode, |
275 | | const std::vector<std::shared_ptr<PType>>& params, |
276 | 13 | const std::shared_ptr<RType>& result) { |
277 | | // TODO(neil) Currently, the execution phase is not yet implemented, so it'd be immature to |
278 | | // code for error-check here. Once it is implemented, we'll know what to check. |
279 | 0 | if (VLOG_IS_ON(3)) { |
280 | 0 | LOG(INFO) << "Executing opcode " << int(opcode); |
281 | 0 | } |
282 | 13 | return Status::OK(); |
283 | 13 | } _ZN2yb4bfpg9BFExecApiINS0_11BFTestValueES2_NSt3__16vectorENS3_9allocatorEE10CheckErrorENS0_8BFOpcodeERKNS4_INS3_10shared_ptrIS2_EENS5_IS9_EEEERKS9_ Line | Count | Source | 276 | 13 | const std::shared_ptr<RType>& result) { | 277 | | // TODO(neil) Currently, the execution phase is not yet implemented, so it'd be immature to | 278 | | // code for error-check here. Once it is implemented, we'll know what to check. | 279 | 0 | if (VLOG_IS_ON(3)) { | 280 | 0 | LOG(INFO) << "Executing opcode " << int(opcode); | 281 | 0 | } | 282 | 13 | return Status::OK(); | 283 | 13 | } |
Unexecuted instantiation: _ZN2yb4bfpg9BFExecApiINS_7QLValueES2_NSt3__16vectorENS3_9allocatorEE10CheckErrorENS0_8BFOpcodeERKNS4_INS3_10shared_ptrIS2_EENS5_IS9_EEEERKS9_ |
284 | | |
285 | | //------------------------------------------------------------------------------------------------ |
286 | | // Runs the associated entry in the table of function pointers on the given raw pointers. |
287 | | // kBFExecFuncsRaw[opcode](args) |
288 | | static Status ExecPgsqlOpcode(BFOpcode opcode, |
289 | | const std::vector<PType*>& params, |
290 | 0 | RType *result) { |
291 | | // TODO(neil) There has to be some sanity error check here. |
292 | 0 | RETURN_NOT_OK(CheckError(opcode, params, result)); |
293 | 0 | Status s = kBFExecFuncsRaw[static_cast<int>(opcode)](params, result); |
294 | 0 | VLOG(3) << "Executed builtin call(" << int(opcode) << "). Status: " << s.ToString(); |
295 | 0 | return s; |
296 | 0 | } |
297 | | |
298 | | static Status CheckError(BFOpcode opcode, |
299 | | const std::vector<PType*>& params, |
300 | 0 | RType *result) { |
301 | | // TODO(neil) Currently, the execution phase is not yet implemented, so it'd be immature to |
302 | | // code for error-check here. Once it is implemented, we'll know what to check. |
303 | 0 | if (VLOG_IS_ON(3)) { |
304 | 0 | LOG(INFO) << "Executing opcode " << int(opcode); |
305 | 0 | } |
306 | 0 | return Status::OK(); |
307 | 0 | } |
308 | | |
309 | | //------------------------------------------------------------------------------------------------ |
310 | | // Runs the associated entry in the table of function pointers on the given raw pointers. |
311 | | // kBFExecFuncsRefAndRaw[opcode](args) |
312 | | static Status ExecPgsqlOpcode(BFOpcode opcode, |
313 | | std::vector<PType> *params, |
314 | 0 | RType *result) { |
315 | | // TODO(neil) There has to be some sanity error check here. |
316 | 0 | RETURN_NOT_OK(CheckError(opcode, params, result)); |
317 | 0 | Status s = kBFExecFuncsRefAndRaw[static_cast<int>(opcode)](params, result); |
318 | 0 | VLOG(3) << "Executed builtin call(" << int(opcode) << "). Status: " << s.ToString(); |
319 | 0 | return s; |
320 | 0 | } |
321 | | |
322 | | static Status CheckError(BFOpcode opcode, |
323 | | std::vector<PType> *params, |
324 | 0 | RType *result) { |
325 | | // TODO(neil) Currently, the execution phase is not yet implemented, so it'd be immature to |
326 | | // code for error-check here. Once it is implemented, we'll know what to check. |
327 | 0 | if (VLOG_IS_ON(3)) { |
328 | 0 | LOG(INFO) << "Executing opcode " << int(opcode); |
329 | 0 | } |
330 | 0 | return Status::OK(); |
331 | 0 | } |
332 | | }; |
333 | | |
334 | | //-------------------------------------------------------------------------------------------------- |
335 | | // This class is conveniently and ONLY for testing purpose. It executes builtin calls by names |
336 | | // instead of opcode. |
337 | | template<typename PType, |
338 | | typename RType, |
339 | | template<typename, typename> class CType = std::vector, |
340 | | template<typename> class AType = std::allocator> |
341 | | class BFExecImmediateApi : public BFExecApi<PType, RType, CType, AType> { |
342 | | public: |
343 | | // Interface for shared_ptr. |
344 | | static Status ExecPgsqlFunc(const string& ql_name, |
345 | | const std::vector<std::shared_ptr<PType>>& params, |
346 | 8 | const std::shared_ptr<RType>& result) { |
347 | 8 | BFOpcode opcode; |
348 | 8 | const BFDecl *bfdecl; |
349 | 8 | RETURN_NOT_OK((FindOpcode<std::vector<std::shared_ptr<PType>>, const std::shared_ptr<RType>&>( |
350 | 8 | ql_name, params, &opcode, &bfdecl, result))); |
351 | 8 | return BFExecApi<PType, RType>::ExecPgsqlOpcode(opcode, params, result); |
352 | 8 | } |
353 | | |
354 | | // Interface for raw pointer. |
355 | | static Status ExecPgsqlFunc(const string& ql_name, |
356 | | const std::vector<PType*>& params, |
357 | | RType *result) { |
358 | | BFOpcode opcode; |
359 | | const BFDecl *bfdecl; |
360 | | RETURN_NOT_OK( |
361 | | (FindOpcode<std::vector<PType*>, RType*>(ql_name, params, &opcode, &bfdecl, result))); |
362 | | return BFExecApi<PType, RType>::ExecPgsqlOpcode(opcode, params, result); |
363 | | } |
364 | | }; |
365 | | |
366 | | } // namespace bfpg |
367 | | } // namespace yb |
368 | | |
369 | | //-------------------------------------------------------------------------------------------------- |
370 | | // Generated tables "kBFExecFuncs" and "kBFExecFuncsRaw". |
371 | | // Because the tables must be initialized after the specification for "class BFExecApi", we have to |
372 | | // include header file "gen_bfunc_table.h" at the end of this file. |
373 | | #include "yb/bfpg/gen_bfunc_table.h" |
374 | | |
375 | | #endif // YB_BFPG_BFPG_H |