YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/bfpg/bfpg-test.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) YugaByte, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4
// in compliance with the License.  You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software distributed under the License
9
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
// or implied.  See the License for the specific language governing permissions and limitations
11
// under the License.
12
//
13
// This is a simple test to verify the correctness of builtin function library.
14
15
#include <memory>
16
#include <vector>
17
18
#include "yb/bfpg/bfpg.h"
19
20
#include "yb/common/ql_value.h"
21
22
#include "yb/util/test_util.h"
23
24
namespace yb {
25
namespace bfpg {
26
27
using std::shared_ptr;
28
using std::make_shared;
29
using std::to_string;
30
using std::vector;
31
//--------------------------------------------------------------------------------------------------
32
// BFTestValue is a data value to be used with builtin library for both phases - compilation and
33
// execution. Note that the plan is to have two different data structures for two different.
34
// - PGSQL treenode is used during compilation.
35
// - QLValue is used during execution.
36
class BFTestValue : public QLValue {
37
 public:
38
  typedef std::shared_ptr<BFTestValue> SharedPtr;
39
  typedef std::shared_ptr<const BFTestValue> SharedPtrConst;
40
41
13
  BFTestValue() : QLValue() {
42
13
    ql_type_id_ = DataType::UNKNOWN_DATA;
43
13
  }
44
45
69
  virtual DataType ql_type_id() const {
46
69
    return ql_type_id_;
47
69
  }
48
49
  virtual void set_ql_type_id(DataType ql_type_id) {
49
49
    ql_type_id_ = ql_type_id;
50
49
  }
51
52
 private:
53
  DataType ql_type_id_;
54
};
55
56
//--------------------------------------------------------------------------------------------------
57
// Construct BFApiTest classes from the builtin template. This test is kept to be generic for all
58
// applications, so it defines its own type. For specific application, appropriate datatypes should
59
// be used in its tests.
60
using BFCompileApiTest = BFCompileApi<BFTestValue, BFTestValue>;
61
using BFExecApiTest = BFExecImmediateApi<BFTestValue, BFTestValue>;
62
63
//--------------------------------------------------------------------------------------------------
64
// This is the driver for bfpg-test.
65
class BfPgsqlTest : public YBTest {
66
 public:
67
  // Constructor and destructor.
68
3
  BfPgsqlTest() {
69
3
  }
70
71
3
  virtual ~BfPgsqlTest() {
72
3
  }
73
74
  //------------------------------------------------------------------------------------------------
75
  // Test start and cleanup functions.
76
3
  void SetUp() override {
77
3
    YBTest::SetUp();
78
3
  }
79
80
3
  void TearDown() override {
81
3
    YBTest::TearDown();
82
3
  }
83
84
  // Convert the param values to datatypes that are specified by "bfdecl".
85
  Status ConvertParams(const BFDecl *bfdecl,
86
                       const vector<BFTestValue::SharedPtr>& params,
87
2
                       vector<BFTestValue::SharedPtr> *converted_params) {
88
2
    const auto pcount = params.size();
89
2
    converted_params->resize(pcount);
90
2
    const std::vector<DataType>& ptypes = bfdecl->param_types();
91
92
2
    bool is_variadic = false;
93
2
    vector<BFTestValue::SharedPtr> cast_params(2);
94
6
    for (size_t pindex = 0; pindex < pcount; pindex++) {
95
4
      if (is_variadic || ptypes[pindex] == DataType::TYPEARGS) {
96
        // No conversion is needed for the rest of the arguments.
97
0
        is_variadic = true;
98
0
        (*converted_params)[pindex] = params[pindex];
99
100
4
      } else if (params[pindex]->ql_type_id() == ptypes[pindex]) {
101
2
        (*converted_params)[pindex] = params[pindex];
102
103
2
      } else {
104
        // Casting is needed.
105
2
        BFTestValue::SharedPtr converted_param = make_shared<BFTestValue>();
106
2
        converted_param->set_ql_type_id(ptypes[pindex]);
107
108
        // Converting params.
109
2
        cast_params[0] = params[pindex];
110
2
        cast_params[1] = converted_param;
111
2
        RETURN_NOT_OK(BFExecApiTest::ExecPgsqlFunc(
112
2
            bfpg::kCastFuncName, cast_params, converted_param));
113
114
        // Save converted value.
115
2
        (*converted_params)[pindex] = converted_param;
116
2
      }
117
4
    }
118
119
2
    return Status::OK();
120
2
  }
121
};
122
123
//--------------------------------------------------------------------------------------------------
124
// The following test cases generally go through the following steps.
125
//
126
// - Use the BFTestValue functions to set the values and datatypes for parameter.
127
// - Similarly, use the BFTestValue functions to set the datatypes for return result.
128
//   NOTE: When the return type is not set, builtin-library will set it. On the other hand, if the
129
//   return type is set, the builtin-library will check if the datatype is compatible with the
130
//   definition of the builtin function.
131
// - Call FindPgsqlOpcode() to find the opcode. This step should be done during compilation.
132
// - Call ExecPgsqlFunc() to run the builtin function. This step will be done during execution.
133
134
// Test calls to functions with arguments whose datatypes are an exact matched to the signature.
135
1
TEST_F(BfPgsqlTest, TestExactMatchSignature) {
136
1
  BFOpcode opcode;
137
1
  const BFDecl *bfdecl;
138
139
1
  BFTestValue::SharedPtr result = make_shared<BFTestValue>();
140
1
  BFTestValue::SharedPtr param0 = make_shared<BFTestValue>();
141
1
  BFTestValue::SharedPtr param1 = make_shared<BFTestValue>();
142
143
  // Use raw pointer to test the API for raw pointers.
144
1
  vector<BFTestValue::SharedPtr> params = { param0, param1 };
145
146
  //------------------------------------------------------------------------------------------------
147
  // Test cases of exact match calls for integer functions.
148
1
  param0->set_ql_type_id(DataType::INT64);
149
1
  param1->set_ql_type_id(DataType::INT64);
150
151
  // Test Case 1: Not specify the return type by setting it to NULL.
152
  //    UNKNOWN = INT64 + INT64.
153
  // Set result type to be unknown and let builtin library resolve its type.
154
1
  result->set_ql_type_id(DataType::UNKNOWN_DATA);
155
156
  // Initialize parameter values.
157
1
  int int_val1 = 2133;
158
1
  int int_val2 = 1234;
159
1
  param0->set_int64_value(int_val1);
160
1
  param1->set_int64_value(int_val2);
161
162
  // Find and execute the opcode.
163
1
  ASSERT_OK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
164
1
  ASSERT_OK(BFExecApiTest::ExecPgsqlOpcode(opcode, params, result));
165
166
  // Write the result to an integer and check the result.
167
1
  int expected_int_result = int_val1 + int_val2;
168
1
  auto return_int_result = result->int64_value();
169
1
  ASSERT_EQ(return_int_result, expected_int_result);
170
171
  // Test Case 2: The return type is exact match
172
  //    INT64 = INT64 + INT64.
173
  // Similar to test #1, but set result type to INT32 let builtin library does the type-checking.
174
1
  result->set_ql_type_id(DataType::INT64);
175
1
  int_val1 = 4133;
176
1
  int_val2 = 7234;
177
1
  param0->set_int64_value(int_val1);
178
1
  param1->set_int64_value(int_val2);
179
1
  ASSERT_OK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
180
1
  ASSERT_EQ(result->ql_type_id(), DataType::INT64);
181
1
  ASSERT_OK(BFExecApiTest::ExecPgsqlOpcode(opcode, params, result));
182
183
1
  expected_int_result = int_val1 + int_val2;
184
1
  return_int_result = result->int64_value();
185
1
  ASSERT_EQ(return_int_result, expected_int_result);
186
187
  // Test Case 3: The return type is compatible to force a conversion for return value.
188
  //    INT16 = INT64 + INT64.
189
  // Similar to test #1, but set result type to INT16.
190
1
  BFTestValue::SharedPtr temp_result = make_shared<BFTestValue>();
191
1
  temp_result->set_ql_type_id(DataType::UNKNOWN_DATA);
192
193
1
  int_val1 = 3133;
194
1
  int_val2 = 9234;
195
1
  param0->set_int64_value(int_val1);
196
1
  param1->set_int64_value(int_val2);
197
1
  ASSERT_OK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, temp_result));
198
1
  ASSERT_EQ(temp_result->ql_type_id(), DataType::INT64);
199
1
  ASSERT_OK(BFExecApiTest::ExecPgsqlOpcode(opcode, params, temp_result));
200
201
  // Convert int64 value (temp_result) to int16 value (result).
202
1
  result->set_ql_type_id(DataType::INT16);
203
1
  vector<BFTestValue::SharedPtr> temp_params = { temp_result, result };
204
1
  ASSERT_OK(BFExecApiTest::ExecPgsqlFunc(bfpg::kCastFuncName, temp_params, result));
205
206
  // Check result.
207
1
  expected_int_result = int_val1 + int_val2;
208
1
  return_int_result = result->int16_value();
209
1
  ASSERT_EQ(return_int_result, expected_int_result);
210
211
  //------------------------------------------------------------------------------------------------
212
  // Test exact match calls for double functions.
213
  // The steps in this test is the same as for integer.
214
  // Parameter type is now set to double.
215
1
  param0->set_ql_type_id(DataType::DOUBLE);
216
1
  param1->set_ql_type_id(DataType::DOUBLE);
217
218
  // - Case 1: Not specify the return type by setting it to NULL.
219
  //     UNKNOWN = DOUBLE + DOUBLE.
220
1
  result->set_ql_type_id(DataType::UNKNOWN_DATA);
221
222
1
  double d_val1 = 777.7;
223
1
  double d_val2 = 1111.7;
224
1
  param0->set_double_value(d_val1);
225
1
  param1->set_double_value(d_val2);
226
1
  ASSERT_OK(BFExecApiTest::ExecPgsqlFunc("+", params, result));
227
1
  ASSERT_EQ(result->ql_type_id(), DataType::DOUBLE);
228
229
  // Write the return value to an int so that we can run EQ check.
230
1
  expected_int_result = d_val1 + d_val2;
231
1
  return_int_result = result->double_value();
232
1
  ASSERT_EQ(return_int_result, expected_int_result);
233
234
  // - Case 2: Have exact match return type.
235
  //     DOUBLE = DOUBLE + DOUBLE.
236
1
  result->set_ql_type_id(DataType::DOUBLE);
237
238
1
  d_val1 = 999.9;
239
1
  d_val2 = 3333.3;
240
1
  param0->set_double_value(d_val1);
241
1
  param1->set_double_value(d_val2);
242
1
  ASSERT_OK(BFExecApiTest::ExecPgsqlFunc("+", params, result));
243
244
1
  expected_int_result = d_val1 + d_val2;
245
1
  return_int_result = result->double_value();
246
1
  ASSERT_EQ(return_int_result, expected_int_result);
247
248
  // - Case 3: Have compatible return type.
249
  //     FLOAT = DOUBLE + DOUBLE.
250
1
  d_val1 = 888.9;
251
1
  d_val2 = 8888.3;
252
1
  param0->set_double_value(d_val1);
253
1
  param1->set_double_value(d_val2);
254
255
  // Execute (double + double) and convert double(temp_result) to float(result).
256
1
  result->set_ql_type_id(DataType::FLOAT);
257
1
  temp_result->set_ql_type_id(DataType::DOUBLE);
258
1
  ASSERT_OK(BFExecApiTest::ExecPgsqlFunc("+", params, temp_result));
259
1
  ASSERT_OK(BFExecApiTest::ExecPgsqlFunc("cast", temp_params, result));
260
261
1
  expected_int_result = d_val1 + d_val2;
262
1
  return_int_result = result->float_value();
263
1
  ASSERT_EQ(return_int_result, expected_int_result);
264
265
  //------------------------------------------------------------------------------------------------
266
  // Test exact match calls for string functions.
267
  // Test case: STRING = STRING + STRING
268
1
  result->set_ql_type_id(DataType::STRING);
269
270
1
  param0->set_ql_type_id(DataType::STRING);
271
1
  param1->set_ql_type_id(DataType::STRING);
272
1
  param0->set_string_value("First part of String. ");
273
1
  param1->set_string_value("Second part of String.");
274
275
1
  ASSERT_OK(BFExecApiTest::ExecPgsqlFunc("+", params, result));
276
1
  ASSERT_EQ(result->string_value(), "First part of String. Second part of String.");
277
1
}
278
279
// Test calls to functions with arguments whose datatypes are convertible but not an exact match to
280
// the function signature.
281
1
TEST_F(BfPgsqlTest, TestCompatibleSignature) {
282
1
  BFOpcode opcode;
283
1
  const BFDecl *bfdecl;
284
285
1
  BFTestValue::SharedPtr result = make_shared<BFTestValue>();
286
1
  BFTestValue::SharedPtr param0 = make_shared<BFTestValue>();
287
1
  BFTestValue::SharedPtr param1 = make_shared<BFTestValue>();
288
289
  // Use shared pointer to test the API for shared_ptr.
290
1
  vector<BFTestValue::SharedPtr> params = { param0, param1 };
291
1
  vector<BFTestValue::SharedPtr> converted_params;
292
293
  //------------------------------------------------------------------------------------------------
294
  // Test case: Passing (STRING, INT16) to (STRING, DOUBLE)
295
296
  // Set result type to be unknown and let builtin library resolve its type.
297
1
  result->set_ql_type_id(DataType::UNKNOWN_DATA);
298
299
  // Initialize parameter datatypes and values.
300
1
  param0->set_ql_type_id(DataType::STRING);
301
1
  param1->set_ql_type_id(DataType::INT16);
302
1
  param0->set_string_value("The value is ");
303
1
  param1->set_int16_value(100);
304
305
  // Find the opcode.
306
1
  ASSERT_OK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
307
1
  ASSERT_OK(ConvertParams(bfdecl, params, &converted_params));
308
309
  // Execute the opcode.
310
1
  ASSERT_OK(BFExecApiTest::ExecPgsqlOpcode(opcode, converted_params, result));
311
312
  // Write the result to a string and check the result.
313
1
  string expected_result = string("The value is ") + to_string(100.);
314
1
  string return_result = result->string_value();
315
1
  ASSERT_EQ(return_result, expected_result);
316
317
  //------------------------------------------------------------------------------------------------
318
  // Test case: Passing (INT64, STRING) to (DOUBLE, STRING)
319
320
  // Set result type to be unknown and let builtin library resolve its type.
321
1
  result->set_ql_type_id(DataType::UNKNOWN_DATA);
322
323
  // Initialize parameter datatypes and values.
324
1
  param0->set_ql_type_id(DataType::INT64);
325
1
  param1->set_ql_type_id(DataType::STRING);
326
1
  param0->set_int64_value(100);
327
1
  param1->set_string_value(" is the value");
328
329
  // Find the opcode.
330
1
  ASSERT_OK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
331
1
  ASSERT_OK(ConvertParams(bfdecl, params, &converted_params));
332
333
  // Execute the opcode.
334
1
  ASSERT_OK(BFExecApiTest::ExecPgsqlOpcode(opcode, converted_params, result));
335
336
  // Write the result to a string and check the result.
337
1
  expected_result = to_string(100.) + string(" is the value");
338
1
  return_result = result->string_value();
339
1
  ASSERT_EQ(return_result, expected_result);
340
1
}
341
342
// Test bad function calls.
343
1
TEST_F(BfPgsqlTest, TestErroneousFuncCalls) {
344
1
  BFOpcode opcode;
345
1
  const BFDecl *bfdecl;
346
347
1
  BFTestValue::SharedPtr result = make_shared<BFTestValue>();
348
1
  vector<BFTestValue::SharedPtr> params;
349
350
  // Invalid function name.
351
1
  ASSERT_NOK(BFCompileApiTest::FindPgsqlOpcode("wrong_name", params, &opcode, &bfdecl, result));
352
353
  //------------------------------------------------------------------------------------------------
354
  // Test for invalid parameter count.
355
  // Passing 0 argument to '+', which takes exactly 2 arguments.
356
1
  ASSERT_NOK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
357
358
  // Passing 1 argument to '+', which takes exactly 2 arguments.
359
1
  BFTestValue::SharedPtr param0 = make_shared<BFTestValue>();
360
1
  params.push_back(param0);
361
1
  param0->set_ql_type_id(DataType::INT32);
362
1
  ASSERT_NOK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
363
364
1
  BFTestValue::SharedPtr param1 = make_shared<BFTestValue>();
365
1
  param1->set_ql_type_id(DataType::INT32);
366
1
  params.push_back(param1);
367
368
  // Passing 3 arguments to '+', which takes exactly 2 arguments.
369
1
  BFTestValue::SharedPtr param2 = make_shared<BFTestValue>();
370
1
  param2->set_ql_type_id(DataType::INT32);
371
1
  params.push_back(param2);
372
1
  ASSERT_NOK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
373
374
  //------------------------------------------------------------------------------------------------
375
  // Test for invalid parameter types.
376
1
  params.resize(2);
377
1
  param0->set_ql_type_id(DataType::INT32);
378
1
  param1->set_ql_type_id(DataType::BOOL);
379
1
  ASSERT_NOK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
380
381
1
  param0->set_ql_type_id(DataType::BOOL);
382
1
  param1->set_ql_type_id(DataType::INT32);
383
1
  ASSERT_NOK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
384
385
  //------------------------------------------------------------------------------------------------
386
  // Test for invalid return type.
387
  // The builtin call will set the return-type after its evaluate the function calls. Howver, if
388
  // the return_type is set by application, the builtin-call will check if the given return type
389
  // is compatible.
390
1
  param0->set_ql_type_id(DataType::INT32);
391
1
  param1->set_ql_type_id(DataType::INT32);
392
393
1
  result->set_ql_type_id(DataType::STRING);
394
1
  ASSERT_NOK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
395
396
1
  result->set_ql_type_id(DataType::BOOL);
397
1
  ASSERT_NOK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
398
399
  //------------------------------------------------------------------------------------------------
400
  // Test for ambiguous signature - Too many builtin match the signature of a function call.
401
  // The following can be matched with both signature INT(INT, INT) and DOUBLE(DOUBLE, DOUBLE).
402
1
  param0->set_ql_type_id(DataType::INT8);
403
1
  param1->set_ql_type_id(DataType::INT8);
404
1
  ASSERT_NOK(BFCompileApiTest::FindPgsqlOpcode("+", params, &opcode, &bfdecl, result));
405
1
}
406
407
} // namespace bfpg
408
} // namespace yb