YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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