YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/bfql/bfql-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/bfql/bfql.h"
19
20
#include "yb/common/ql_value.h"
21
22
#include "yb/util/net/net_util.h"
23
#include "yb/util/status_log.h"
24
#include "yb/util/test_util.h"
25
26
namespace yb {
27
namespace bfql {
28
29
using std::shared_ptr;
30
using std::make_shared;
31
using std::to_string;
32
using std::vector;
33
using std::numeric_limits;
34
//--------------------------------------------------------------------------------------------------
35
// BFTestValue is a data value to be used with builtin library for both phases - compilation and
36
// execution. Note that the plan is to have two different data structures for two different.
37
// - QL treenode is used during compilation.
38
// - QLValue is used during execution.
39
class BFTestValue : public QLValue {
40
 public:
41
  typedef std::shared_ptr<BFTestValue> SharedPtr;
42
  typedef std::shared_ptr<const BFTestValue> SharedPtrConst;
43
44
127
  BFTestValue() : QLValue() {
45
127
    ql_type_id_ = DataType::UNKNOWN_DATA;
46
127
  }
47
48
265
  virtual DataType ql_type_id() const {
49
265
    return ql_type_id_;
50
265
  }
51
154
  virtual void set_ql_type_id(DataType ql_type_id) {
52
154
    ql_type_id_ = ql_type_id;
53
154
  }
54
55
 private:
56
  DataType ql_type_id_;
57
};
58
59
//--------------------------------------------------------------------------------------------------
60
// Construct BFApiTest classes from the builtin template. This test is kept to be generic for all
61
// applications, so it defines its own type. For specific application, appropriate datatypes should
62
// be used in its tests.
63
using BFCompileApiTest = BFCompileApi<BFTestValue, BFTestValue>;
64
using BFExecApiTest = BFExecImmediateApi<BFTestValue, BFTestValue>;
65
66
//--------------------------------------------------------------------------------------------------
67
// This is the driver for bfql-test.
68
class BfqlTest : public YBTest {
69
 public:
70
  // Constructor and destructor.
71
6
  BfqlTest() {
72
6
  }
73
74
6
  virtual ~BfqlTest() {
75
6
  }
76
77
  //------------------------------------------------------------------------------------------------
78
  // Test start and cleanup functions.
79
6
  void SetUp() override {
80
6
    YBTest::SetUp();
81
6
  }
82
83
6
  void TearDown() override {
84
6
    YBTest::TearDown();
85
6
  }
86
87
  // Convert the param values to datatypes that are specified by "bfdecl".
88
  Status ConvertParams(const BFDecl *bfdecl,
89
                       const vector<BFTestValue::SharedPtr>& params,
90
2
                       vector<BFTestValue::SharedPtr> *converted_params) {
91
2
    const auto pcount = params.size();
92
2
    converted_params->resize(pcount);
93
2
    const std::vector<DataType>& ptypes = bfdecl->param_types();
94
95
2
    bool is_variadic = false;
96
2
    vector<BFTestValue::SharedPtr> cast_params(2);
97
6
    for (size_t pindex = 0; pindex < pcount; pindex++) {
98
4
      if (is_variadic || ptypes[pindex] == DataType::TYPEARGS) {
99
        // No conversion is needed for the rest of the arguments.
100
0
        is_variadic = true;
101
0
        (*converted_params)[pindex] = params[pindex];
102
103
4
      } else if (params[pindex]->ql_type_id() == ptypes[pindex]) {
104
2
        (*converted_params)[pindex] = params[pindex];
105
106
2
      } else {
107
        // Casting is needed.
108
2
        BFTestValue::SharedPtr converted_param = make_shared<BFTestValue>();
109
2
        converted_param->set_ql_type_id(ptypes[pindex]);
110
111
        // Converting params.
112
2
        cast_params[0] = params[pindex];
113
2
        cast_params[1] = converted_param;
114
2
        RETURN_NOT_OK(BFExecApiTest::ExecQLFunc(bfql::kCastFuncName, cast_params, converted_param));
115
116
        // Save converted value.
117
2
        (*converted_params)[pindex] = converted_param;
118
2
      }
119
4
    }
120
121
2
    return Status::OK();
122
2
  }
123
124
  // Test token() and partition_hash().
125
  template <typename ResultVal, typename BuiltinFunction>
126
  void testPartitionHash(const std::string& name, DataType return_type, ResultVal result_val,
127
2
                         BuiltinFunction builtin_function) {
128
2
    BFTestValue::SharedPtr result = make_shared<BFTestValue>();
129
2
    vector<BFTestValue::SharedPtr> test_params(8);
130
18
    for (int pindex = 0; pindex < 8; pindex++) {
131
16
      test_params[pindex] = make_shared<BFTestValue>();
132
16
    }
133
2
    test_params[0]->set_int8_value(100);
134
2
    test_params[1]->set_int16_value(200);
135
2
    test_params[2]->set_int32_value(300);
136
2
    test_params[3]->set_int64_value(400);
137
2
    test_params[4]->set_timestamp_value(500);
138
2
    test_params[5]->set_string_value("600");
139
2
    test_params[7]->set_binary_value("700");
140
141
2
    Uuid uuid = ASSERT_RESULT(Uuid::FromString("80000000-0000-0000-0000-000000000000"));
142
2
    test_params[6]->set_uuid_value(uuid);
143
144
    // Convert test_params to params.
145
2
    vector<BFTestValue::SharedPtr> params(test_params.begin(), test_params.end());
146
147
    // Use wrong return type and expect error.
148
    // NOTES:
149
    // - BFExecApiTest::ExecQLFunc("builin_name") will combine the two steps of finding and
150
    //   executing opcode into one function call. This is only convenient for testing. In actual
151
    //   code, except execute-immediate feature, this process is divided into two steps.
152
2
    result->set_ql_type_id(DataType::STRING);
153
2
    ASSERT_NOK(BFExecApiTest::ExecQLFunc(name, params, result));
154
155
    // Use correct return type.
156
2
    result->set_ql_type_id(return_type);
157
2
    ASSERT_OK(BFExecApiTest::ExecQLFunc(name, params, result));
158
159
    // Call the C++ function directly and verify result.
160
2
    BFTestValue::SharedPtr expected_result = make_shared<BFTestValue>();
161
2
    ASSERT_OK(builtin_function(params, expected_result));
162
2
    ASSERT_EQ(result_val(result), result_val(expected_result));
163
164
    // Convert shared_ptr to raw pointer to test the API for raw pointers.
165
2
    BFTestValue *raw_result = result.get();
166
2
    vector<BFTestValue*> raw_params(8);
167
18
    for (int pindex = 0; pindex < 8; pindex++) {
168
16
      raw_params[pindex] = params[pindex].get();
169
16
    }
170
171
    // Use wrong return type and expect error.
172
2
    raw_result->set_ql_type_id(DataType::STRING);
173
2
    ASSERT_NOK(BFExecApiTest::ExecQLFunc(name, raw_params, raw_result));
174
175
    // Use correct return type.
176
2
    raw_result->set_ql_type_id(return_type);
177
2
    ASSERT_OK(BFExecApiTest::ExecQLFunc(name, raw_params, raw_result));
178
179
    // Call the C++ function directly and verify result.
180
2
    ASSERT_OK(builtin_function(params, expected_result));
181
2
    ASSERT_EQ(result_val(result), result_val(expected_result));
182
2
  }
bfql-test.cc:_ZN2yb4bfql8BfqlTest17testPartitionHashIZNS0_38BfqlTest_TestVariadicBuiltinToken_Test8TestBodyEvE3$_0ZNS3_8TestBodyEvE3$_1EEvRKNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEENS_8DataTypeET_T0_
Line
Count
Source
127
1
                         BuiltinFunction builtin_function) {
128
1
    BFTestValue::SharedPtr result = make_shared<BFTestValue>();
129
1
    vector<BFTestValue::SharedPtr> test_params(8);
130
9
    for (int pindex = 0; pindex < 8; pindex++) {
131
8
      test_params[pindex] = make_shared<BFTestValue>();
132
8
    }
133
1
    test_params[0]->set_int8_value(100);
134
1
    test_params[1]->set_int16_value(200);
135
1
    test_params[2]->set_int32_value(300);
136
1
    test_params[3]->set_int64_value(400);
137
1
    test_params[4]->set_timestamp_value(500);
138
1
    test_params[5]->set_string_value("600");
139
1
    test_params[7]->set_binary_value("700");
140
141
1
    Uuid uuid = ASSERT_RESULT(Uuid::FromString("80000000-0000-0000-0000-000000000000"));
142
1
    test_params[6]->set_uuid_value(uuid);
143
144
    // Convert test_params to params.
145
1
    vector<BFTestValue::SharedPtr> params(test_params.begin(), test_params.end());
146
147
    // Use wrong return type and expect error.
148
    // NOTES:
149
    // - BFExecApiTest::ExecQLFunc("builin_name") will combine the two steps of finding and
150
    //   executing opcode into one function call. This is only convenient for testing. In actual
151
    //   code, except execute-immediate feature, this process is divided into two steps.
152
1
    result->set_ql_type_id(DataType::STRING);
153
1
    ASSERT_NOK(BFExecApiTest::ExecQLFunc(name, params, result));
154
155
    // Use correct return type.
156
1
    result->set_ql_type_id(return_type);
157
1
    ASSERT_OK(BFExecApiTest::ExecQLFunc(name, params, result));
158
159
    // Call the C++ function directly and verify result.
160
1
    BFTestValue::SharedPtr expected_result = make_shared<BFTestValue>();
161
1
    ASSERT_OK(builtin_function(params, expected_result));
162
1
    ASSERT_EQ(result_val(result), result_val(expected_result));
163
164
    // Convert shared_ptr to raw pointer to test the API for raw pointers.
165
1
    BFTestValue *raw_result = result.get();
166
1
    vector<BFTestValue*> raw_params(8);
167
9
    for (int pindex = 0; pindex < 8; pindex++) {
168
8
      raw_params[pindex] = params[pindex].get();
169
8
    }
170
171
    // Use wrong return type and expect error.
172
1
    raw_result->set_ql_type_id(DataType::STRING);
173
1
    ASSERT_NOK(BFExecApiTest::ExecQLFunc(name, raw_params, raw_result));
174
175
    // Use correct return type.
176
1
    raw_result->set_ql_type_id(return_type);
177
1
    ASSERT_OK(BFExecApiTest::ExecQLFunc(name, raw_params, raw_result));
178
179
    // Call the C++ function directly and verify result.
180
1
    ASSERT_OK(builtin_function(params, expected_result));
181
1
    ASSERT_EQ(result_val(result), result_val(expected_result));
182
1
  }
bfql-test.cc:_ZN2yb4bfql8BfqlTest17testPartitionHashIZNS0_46BfqlTest_TestVariadicBuiltinPartitionHash_Test8TestBodyEvE3$_2ZNS3_8TestBodyEvE3$_3EEvRKNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEENS_8DataTypeET_T0_
Line
Count
Source
127
1
                         BuiltinFunction builtin_function) {
128
1
    BFTestValue::SharedPtr result = make_shared<BFTestValue>();
129
1
    vector<BFTestValue::SharedPtr> test_params(8);
130
9
    for (int pindex = 0; pindex < 8; pindex++) {
131
8
      test_params[pindex] = make_shared<BFTestValue>();
132
8
    }
133
1
    test_params[0]->set_int8_value(100);
134
1
    test_params[1]->set_int16_value(200);
135
1
    test_params[2]->set_int32_value(300);
136
1
    test_params[3]->set_int64_value(400);
137
1
    test_params[4]->set_timestamp_value(500);
138
1
    test_params[5]->set_string_value("600");
139
1
    test_params[7]->set_binary_value("700");
140
141
1
    Uuid uuid = ASSERT_RESULT(Uuid::FromString("80000000-0000-0000-0000-000000000000"));
142
1
    test_params[6]->set_uuid_value(uuid);
143
144
    // Convert test_params to params.
145
1
    vector<BFTestValue::SharedPtr> params(test_params.begin(), test_params.end());
146
147
    // Use wrong return type and expect error.
148
    // NOTES:
149
    // - BFExecApiTest::ExecQLFunc("builin_name") will combine the two steps of finding and
150
    //   executing opcode into one function call. This is only convenient for testing. In actual
151
    //   code, except execute-immediate feature, this process is divided into two steps.
152
1
    result->set_ql_type_id(DataType::STRING);
153
1
    ASSERT_NOK(BFExecApiTest::ExecQLFunc(name, params, result));
154
155
    // Use correct return type.
156
1
    result->set_ql_type_id(return_type);
157
1
    ASSERT_OK(BFExecApiTest::ExecQLFunc(name, params, result));
158
159
    // Call the C++ function directly and verify result.
160
1
    BFTestValue::SharedPtr expected_result = make_shared<BFTestValue>();
161
1
    ASSERT_OK(builtin_function(params, expected_result));
162
1
    ASSERT_EQ(result_val(result), result_val(expected_result));
163
164
    // Convert shared_ptr to raw pointer to test the API for raw pointers.
165
1
    BFTestValue *raw_result = result.get();
166
1
    vector<BFTestValue*> raw_params(8);
167
9
    for (int pindex = 0; pindex < 8; pindex++) {
168
8
      raw_params[pindex] = params[pindex].get();
169
8
    }
170
171
    // Use wrong return type and expect error.
172
1
    raw_result->set_ql_type_id(DataType::STRING);
173
1
    ASSERT_NOK(BFExecApiTest::ExecQLFunc(name, raw_params, raw_result));
174
175
    // Use correct return type.
176
1
    raw_result->set_ql_type_id(return_type);
177
1
    ASSERT_OK(BFExecApiTest::ExecQLFunc(name, raw_params, raw_result));
178
179
    // Call the C++ function directly and verify result.
180
1
    ASSERT_OK(builtin_function(params, expected_result));
181
1
    ASSERT_EQ(result_val(result), result_val(expected_result));
182
1
  }
183
};
184
185
//--------------------------------------------------------------------------------------------------
186
// The following test cases generally go through the following steps.
187
//
188
// - Use the BFTestValue functions to set the values and datatypes for parameter.
189
// - Similarly, use the BFTestValue functions to set the datatypes for return result.
190
//   NOTE: When the return type is not set, builtin-library will set it. On the other hand, if the
191
//   return type is set, the builtin-library will check if the datatype is compatible with the
192
//   definition of the builtin function.
193
// - Call FindQLOpcode() to find the opcode. This step should be done during compilation.
194
// - Call ExecQLFunc() to run the builtin function. This step will be done during execution.
195
196
// Test calls to functions with arguments whose datatypes are an exact matched to the signature.
197
1
TEST_F(BfqlTest, TestExactMatchSignature) {
198
1
  BFOpcode opcode;
199
1
  const BFDecl *bfdecl;
200
201
1
  BFTestValue::SharedPtr result = make_shared<BFTestValue>();
202
1
  BFTestValue::SharedPtr param0 = make_shared<BFTestValue>();
203
1
  BFTestValue::SharedPtr param1 = make_shared<BFTestValue>();
204
205
  // Use raw pointer to test the API for raw pointers.
206
1
  vector<BFTestValue::SharedPtr> params = { param0, param1 };
207
208
  //------------------------------------------------------------------------------------------------
209
  // Test cases of exact match calls for integer functions.
210
1
  param0->set_ql_type_id(DataType::INT64);
211
1
  param1->set_ql_type_id(DataType::INT64);
212
213
  // Test Case 1: Not specify the return type by setting it to NULL.
214
  //    UNKNOWN = INT64 + INT64.
215
  // Set result type to be unknown and let builtin library resolve its type.
216
1
  result->set_ql_type_id(DataType::UNKNOWN_DATA);
217
218
  // Initialize parameter values.
219
1
  int int_val1 = 2133;
220
1
  int int_val2 = 1234;
221
1
  param0->set_int64_value(int_val1);
222
1
  param1->set_int64_value(int_val2);
223
224
  // Find and execute the opcode.
225
1
  ASSERT_OK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
226
1
  ASSERT_OK(BFExecApiTest::ExecQLOpcode(opcode, params, result));
227
228
  // Write the result to an integer and check the result.
229
1
  int expected_int_result = int_val1 + int_val2;
230
1
  auto return_int_result = result->int64_value();
231
1
  ASSERT_EQ(return_int_result, expected_int_result);
232
233
  // Test Case 2: The return type is exact match
234
  //    INT64 = INT64 + INT64.
235
  // Similar to test #1, but set result type to INT32 let builtin library does the type-checking.
236
1
  result->set_ql_type_id(DataType::INT64);
237
1
  int_val1 = 4133;
238
1
  int_val2 = 7234;
239
1
  param0->set_int64_value(int_val1);
240
1
  param1->set_int64_value(int_val2);
241
1
  ASSERT_OK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
242
1
  ASSERT_EQ(result->ql_type_id(), DataType::INT64);
243
1
  ASSERT_OK(BFExecApiTest::ExecQLOpcode(opcode, params, result));
244
245
1
  expected_int_result = int_val1 + int_val2;
246
1
  return_int_result = result->int64_value();
247
1
  ASSERT_EQ(return_int_result, expected_int_result);
248
249
  // Test Case 3: The return type is compatible to force a conversion for return value.
250
  //    INT16 = INT64 + INT64.
251
  // Similar to test #1, but set result type to INT16.
252
1
  BFTestValue::SharedPtr temp_result = make_shared<BFTestValue>();
253
1
  temp_result->set_ql_type_id(DataType::UNKNOWN_DATA);
254
255
1
  int_val1 = 3133;
256
1
  int_val2 = 9234;
257
1
  param0->set_int64_value(int_val1);
258
1
  param1->set_int64_value(int_val2);
259
1
  ASSERT_OK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, temp_result));
260
1
  ASSERT_EQ(temp_result->ql_type_id(), DataType::INT64);
261
1
  ASSERT_OK(BFExecApiTest::ExecQLOpcode(opcode, params, temp_result));
262
263
  // Convert int64 value (temp_result) to int16 value (result).
264
1
  result->set_ql_type_id(DataType::INT16);
265
1
  vector<BFTestValue::SharedPtr> temp_params = { temp_result, result };
266
1
  ASSERT_OK(BFExecApiTest::ExecQLFunc(bfql::kCastFuncName, temp_params, result));
267
268
  // Check result.
269
1
  expected_int_result = int_val1 + int_val2;
270
1
  return_int_result = result->int16_value();
271
1
  ASSERT_EQ(return_int_result, expected_int_result);
272
273
  //------------------------------------------------------------------------------------------------
274
  // Test exact match calls for double functions.
275
  // The steps in this test is the same as for integer.
276
  // Parameter type is now set to double.
277
1
  param0->set_ql_type_id(DataType::DOUBLE);
278
1
  param1->set_ql_type_id(DataType::DOUBLE);
279
280
  // - Case 1: Not specify the return type by setting it to NULL.
281
  //     UNKNOWN = DOUBLE + DOUBLE.
282
1
  result->set_ql_type_id(DataType::UNKNOWN_DATA);
283
284
1
  double d_val1 = 777.7;
285
1
  double d_val2 = 1111.7;
286
1
  param0->set_double_value(d_val1);
287
1
  param1->set_double_value(d_val2);
288
1
  ASSERT_OK(BFExecApiTest::ExecQLFunc("+", params, result));
289
1
  ASSERT_EQ(result->ql_type_id(), DataType::DOUBLE);
290
291
  // Write the return value to an int so that we can run EQ check.
292
1
  expected_int_result = d_val1 + d_val2;
293
1
  return_int_result = result->double_value();
294
1
  ASSERT_EQ(return_int_result, expected_int_result);
295
296
  // - Case 2: Have exact match return type.
297
  //     DOUBLE = DOUBLE + DOUBLE.
298
1
  result->set_ql_type_id(DataType::DOUBLE);
299
300
1
  d_val1 = 999.9;
301
1
  d_val2 = 3333.3;
302
1
  param0->set_double_value(d_val1);
303
1
  param1->set_double_value(d_val2);
304
1
  ASSERT_OK(BFExecApiTest::ExecQLFunc("+", params, result));
305
306
1
  expected_int_result = d_val1 + d_val2;
307
1
  return_int_result = result->double_value();
308
1
  ASSERT_EQ(return_int_result, expected_int_result);
309
310
  // - Case 3: Have compatible return type.
311
  //     FLOAT = DOUBLE + DOUBLE.
312
1
  d_val1 = 888.9;
313
1
  d_val2 = 8888.3;
314
1
  param0->set_double_value(d_val1);
315
1
  param1->set_double_value(d_val2);
316
317
  // Execute (double + double) and convert double(temp_result) to float(result).
318
1
  result->set_ql_type_id(DataType::FLOAT);
319
1
  temp_result->set_ql_type_id(DataType::DOUBLE);
320
1
  ASSERT_OK(BFExecApiTest::ExecQLFunc("+", params, temp_result));
321
1
  ASSERT_OK(BFExecApiTest::ExecQLFunc("cast", temp_params, result));
322
323
1
  expected_int_result = d_val1 + d_val2;
324
1
  return_int_result = result->float_value();
325
1
  ASSERT_EQ(return_int_result, expected_int_result);
326
327
  //------------------------------------------------------------------------------------------------
328
  // Test exact match calls for string functions.
329
  // Test case: STRING = STRING + STRING
330
1
  result->set_ql_type_id(DataType::STRING);
331
332
1
  param0->set_ql_type_id(DataType::STRING);
333
1
  param1->set_ql_type_id(DataType::STRING);
334
1
  param0->set_string_value("First part of String. ");
335
1
  param1->set_string_value("Second part of String.");
336
337
1
  ASSERT_OK(BFExecApiTest::ExecQLFunc("+", params, result));
338
1
  ASSERT_EQ(result->string_value(), "First part of String. Second part of String.");
339
1
}
340
341
// Test calls to functions with arguments whose datatypes are convertible but not an exact match to
342
// the function signature.
343
1
TEST_F(BfqlTest, TestCompatibleSignature) {
344
1
  BFOpcode opcode;
345
1
  const BFDecl *bfdecl;
346
347
1
  BFTestValue::SharedPtr result = make_shared<BFTestValue>();
348
1
  BFTestValue::SharedPtr param0 = make_shared<BFTestValue>();
349
1
  BFTestValue::SharedPtr param1 = make_shared<BFTestValue>();
350
351
  // Use shared pointer to test the API for shared_ptr.
352
1
  vector<BFTestValue::SharedPtr> params = { param0, param1 };
353
1
  vector<BFTestValue::SharedPtr> converted_params;
354
355
  //------------------------------------------------------------------------------------------------
356
  // Test case: Passing (STRING, INT16) to (STRING, DOUBLE)
357
358
  // Set result type to be unknown and let builtin library resolve its type.
359
1
  result->set_ql_type_id(DataType::UNKNOWN_DATA);
360
361
  // Initialize parameter datatypes and values.
362
1
  param0->set_ql_type_id(DataType::STRING);
363
1
  param1->set_ql_type_id(DataType::INT16);
364
1
  param0->set_string_value("The value is ");
365
1
  param1->set_int16_value(100);
366
367
  // Find the opcode.
368
1
  ASSERT_OK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
369
1
  ASSERT_OK(ConvertParams(bfdecl, params, &converted_params));
370
371
  // Execute the opcode.
372
1
  ASSERT_OK(BFExecApiTest::ExecQLOpcode(opcode, converted_params, result));
373
374
  // Write the result to a string and check the result.
375
1
  string expected_result = string("The value is ") + to_string(100.);
376
1
  string return_result = result->string_value();
377
1
  ASSERT_EQ(return_result, expected_result);
378
379
  //------------------------------------------------------------------------------------------------
380
  // Test case: Passing (INT64, STRING) to (DOUBLE, STRING)
381
382
  // Set result type to be unknown and let builtin library resolve its type.
383
1
  result->set_ql_type_id(DataType::UNKNOWN_DATA);
384
385
  // Initialize parameter datatypes and values.
386
1
  param0->set_ql_type_id(DataType::INT64);
387
1
  param1->set_ql_type_id(DataType::STRING);
388
1
  param0->set_int64_value(100);
389
1
  param1->set_string_value(" is the value");
390
391
  // Find the opcode.
392
1
  ASSERT_OK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
393
1
  ASSERT_OK(ConvertParams(bfdecl, params, &converted_params));
394
395
  // Execute the opcode.
396
1
  ASSERT_OK(BFExecApiTest::ExecQLOpcode(opcode, converted_params, result));
397
398
  // Write the result to a string and check the result.
399
1
  expected_result = to_string(100.) + string(" is the value");
400
1
  return_result = result->string_value();
401
1
  ASSERT_EQ(return_result, expected_result);
402
1
}
403
404
// Test variadic function calls (TOKEN).
405
1
TEST_F(BfqlTest, TestVariadicBuiltinToken) {
406
1
  testPartitionHash("token", DataType::INT64,
407
4
                    [](BFTestValue::SharedPtr val) -> int64_t { return val->int64_value(); },
408
1
                    [](const vector<BFTestValue::SharedPtr>& params,
409
2
                       BFTestValue::SharedPtr expected_result) -> Status {
410
2
                      return Token<BFTestValue::SharedPtr, BFTestValue::SharedPtr>(params,
411
2
                                                                                   expected_result);
412
2
                    });
413
1
}
414
415
// Test variadic function calls (PARTITION_HASH).
416
1
TEST_F(BfqlTest, TestVariadicBuiltinPartitionHash) {
417
1
  testPartitionHash("partition_hash", DataType::INT32,
418
4
                    [](BFTestValue::SharedPtr val) -> int32_t { return val->int32_value(); },
419
1
                    [](const vector<BFTestValue::SharedPtr>& params,
420
2
                       BFTestValue::SharedPtr expected_result) -> Status {
421
2
                      return PartitionHash<BFTestValue::SharedPtr, BFTestValue::SharedPtr>(
422
2
                          params, expected_result);
423
2
                    });
424
1
}
425
426
// Test bad function calls.
427
1
TEST_F(BfqlTest, TestErroneousFuncCalls) {
428
1
  BFOpcode opcode;
429
1
  const BFDecl *bfdecl;
430
431
1
  BFTestValue::SharedPtr result = make_shared<BFTestValue>();
432
1
  vector<BFTestValue::SharedPtr> params;
433
434
  // Invalid function name.
435
1
  ASSERT_NOK(BFCompileApiTest::FindQLOpcode("wrong_name", params, &opcode, &bfdecl, result));
436
437
  //------------------------------------------------------------------------------------------------
438
  // Test for invalid parameter count.
439
  // Passing 0 argument to '+', which takes exactly 2 arguments.
440
1
  ASSERT_NOK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
441
442
  // Passing 1 argument to '+', which takes exactly 2 arguments.
443
1
  BFTestValue::SharedPtr param0 = make_shared<BFTestValue>();
444
1
  params.push_back(param0);
445
1
  param0->set_ql_type_id(DataType::INT32);
446
1
  ASSERT_NOK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
447
448
1
  BFTestValue::SharedPtr param1 = make_shared<BFTestValue>();
449
1
  param1->set_ql_type_id(DataType::INT32);
450
1
  params.push_back(param1);
451
452
  // Passing 3 arguments to '+', which takes exactly 2 arguments.
453
1
  BFTestValue::SharedPtr param2 = make_shared<BFTestValue>();
454
1
  param2->set_ql_type_id(DataType::INT32);
455
1
  params.push_back(param2);
456
1
  ASSERT_NOK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
457
458
  //------------------------------------------------------------------------------------------------
459
  // Test for invalid parameter types.
460
1
  params.resize(2);
461
1
  param0->set_ql_type_id(DataType::INT32);
462
1
  param1->set_ql_type_id(DataType::BOOL);
463
1
  ASSERT_NOK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
464
465
1
  param0->set_ql_type_id(DataType::BOOL);
466
1
  param1->set_ql_type_id(DataType::INT32);
467
1
  ASSERT_NOK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
468
469
  //------------------------------------------------------------------------------------------------
470
  // Test for invalid return type.
471
  // The builtin call will set the return-type after its evaluate the function calls. Howver, if
472
  // the return_type is set by application, the builtin-call will check if the given return type
473
  // is compatible.
474
1
  param0->set_ql_type_id(DataType::INT32);
475
1
  param1->set_ql_type_id(DataType::INT32);
476
477
1
  result->set_ql_type_id(DataType::STRING);
478
1
  ASSERT_NOK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
479
480
1
  result->set_ql_type_id(DataType::BOOL);
481
1
  ASSERT_NOK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
482
483
  //------------------------------------------------------------------------------------------------
484
  // Test for ambiguous signature - Too many builtin match the signature of a function call.
485
  // The following can be matched with both signature INT(INT, INT) and DOUBLE(DOUBLE, DOUBLE).
486
1
  param0->set_ql_type_id(DataType::INT8);
487
1
  param1->set_ql_type_id(DataType::INT8);
488
1
  ASSERT_NOK(BFCompileApiTest::FindQLOpcode("+", params, &opcode, &bfdecl, result));
489
1
}
490
491
1
TEST_F(BfqlTest, TestBuiltinToJson) {
492
31
  auto check_tojson = [](const BFTestValue& test_value, const string& expected) -> void {
493
124
    auto result_val = [](BFTestValue::SharedPtr val) -> string {
494
124
                        string result;
495
124
                        common::Jsonb jsonb(val->jsonb_value());
496
124
                        CHECK_OK(jsonb.ToJsonString(&result));
497
124
                        return result;
498
124
                      };
499
500
31
    BFTestValue::SharedPtr result = make_shared<BFTestValue>();
501
31
    vector<BFTestValue::SharedPtr> params(1);
502
31
    params[0] = make_shared<BFTestValue>();
503
31
    *(params[0]->mutable_value()) = test_value.value();
504
505
    // Use wrong return type and expect error.
506
    // NOTES:
507
    // - BFExecApiTest::ExecQLFunc("builin_name") will combine the two steps of finding and
508
    //   executing opcode into one function call. This is only convenient for testing. In actual
509
    //   code, except execute-immediate feature, this process is divided into two steps.
510
31
    result->set_ql_type_id(DataType::STRING);
511
31
    ASSERT_NOK(BFExecApiTest::ExecQLFunc("tojson", params, result));
512
513
    // Use correct return type.
514
31
    result->set_ql_type_id(DataType::JSONB);
515
31
    ASSERT_OK(BFExecApiTest::ExecQLFunc("tojson", params, result));
516
31
    LOG(INFO) << "tojson(" << test_value.ToString() << ")=" << result_val(result);
517
31
    ASSERT_EQ(result_val(result), expected);
518
519
    // Call the C++ function directly and verify result.
520
31
    BFTestValue::SharedPtr expected_result = make_shared<BFTestValue>();
521
31
    ASSERT_OK(ToJson(params[0], expected_result));
522
31
    ASSERT_EQ(result_val(result), result_val(expected_result));
523
31
  };
524
525
  // CQL types: NULL, INT8, INT16, INT32, INT64, STRING, BOOL, FLOAT, DOUBLE, BINARY, TIMESTAMP,
526
  //            DECIMAL, VARINT, INET, LIST, MAP, SET, UUID, TIMEUUID, TUPLE, TYPEARGS,
527
  //            USER_DEFINED_TYPE, FROZEN, DATE, TIME, JSONB,
528
1
  BFTestValue param;
529
530
  // Test simple values.
531
532
1
  param.SetNull();
533
1
  check_tojson(param, "null");
534
535
1
  param.set_int8_value(-100);
536
1
  check_tojson(param, "-100");
537
538
1
  param.set_int16_value(-200);
539
1
  check_tojson(param, "-200");
540
541
1
  param.set_int32_value(-300);
542
1
  check_tojson(param, "-300");
543
544
1
  param.set_int64_value(-400);
545
1
  check_tojson(param, "-400");
546
547
1
  param.set_float_value(124.125f);
548
1
  check_tojson(param, "124.125");
549
550
1
  param.set_float_value(numeric_limits<float>::quiet_NaN());
551
1
  check_tojson(param, "null");
552
553
1
  param.set_float_value(-numeric_limits<float>::quiet_NaN());
554
1
  check_tojson(param, "null");
555
556
1
  param.set_float_value(numeric_limits<float>::infinity());
557
1
  check_tojson(param, "null");
558
559
1
  param.set_float_value(-numeric_limits<float>::infinity());
560
1
  check_tojson(param, "null");
561
562
1
  param.set_double_value(124.125);
563
1
  check_tojson(param, "124.125");
564
565
1
  param.set_bool_value(true);
566
1
  check_tojson(param, "true");
567
568
  // Note: conversion into internal JSON 'double' can overflow.
569
1
  util::Decimal d("-9847.125");
570
1
  param.set_decimal_value(d.EncodeToComparable());
571
1
  check_tojson(param, "-9847.125");
572
573
1
  util::Decimal d2("-987654321");
574
1
  param.set_decimal_value(d2.EncodeToComparable());
575
1
  check_tojson(param, "-987654321");
576
577
  // Note: conversion into internal JSON 'double' can overflow.
578
1
  Result<util::VarInt> varint = util::VarInt::CreateFromString("-1234567890");
579
1
  ASSERT_TRUE(varint.ok());
580
1
  param.set_varint_value(*varint);
581
1
  check_tojson(param, "-1234567890");
582
583
1
  param.set_string_value("value");
584
1
  check_tojson(param, "\"value\"");
585
586
1
  Result<uint32_t> date = DateTime::DateFromString("2018-02-14");
587
1
  ASSERT_TRUE(date.ok());
588
1
  param.set_date_value(*date);
589
1
  check_tojson(param, "\"2018-02-14\"");
590
591
1
  Result<int64_t> time = DateTime::TimeFromString("01:02:03.123456789");
592
1
  ASSERT_TRUE(time.ok());
593
1
  param.set_time_value(*time);
594
1
  check_tojson(param, "\"01:02:03.123456789\"");
595
596
1
  string ts_str = "2018-2-14 13:24:56.987+01:00";
597
1
  Result<Timestamp> ts = DateTime::TimestampFromString(ts_str);
598
1
  ASSERT_OK(ts);
599
1
  param.set_timestamp_value(ts->ToInt64());
600
  // TODO: Check output format.
601
1
  check_tojson(param, "\"2018-02-14T12:24:56.987000+0000\"");
602
603
1
  string uuid_str = "87654321-dead-beaf-0000-deadbeaf0000";
604
1
  Uuid uuid = ASSERT_RESULT(Uuid::FromString(uuid_str));
605
1
  param.set_uuid_value(uuid);
606
1
  check_tojson(param, "\"" + uuid_str + "\"");
607
608
1
  uuid_t linux_time_uuid;
609
1
  uuid_generate_time(linux_time_uuid);
610
1
  Uuid time_uuid(linux_time_uuid);
611
1
  ASSERT_OK(time_uuid.IsTimeUuid());
612
1
  ASSERT_OK(time_uuid.HashMACAddress());
613
1
  param.set_timeuuid_value(time_uuid);
614
1
  string time_uuid_str = time_uuid.ToString();
615
1
  check_tojson(param, "\"" + time_uuid_str + "\"");
616
617
1
  InetAddress addr(ASSERT_RESULT(ParseIpAddress("1.2.3.4")));
618
1
  param.set_inetaddress_value(addr);
619
1
  check_tojson(param, "\"1.2.3.4\"");
620
621
1
  param.set_binary_value("ABC");
622
1
  check_tojson(param, "\"0x414243\"");
623
624
1
  string json_str = "{\"a\":{\"b\":321},\"c\":[{\"d\":123}]}";
625
1
  common::Jsonb jsonb;
626
1
  ASSERT_OK(jsonb.FromString(json_str));
627
1
  param.set_jsonb_value(jsonb.SerializedJsonb());
628
1
  check_tojson(param, json_str);
629
630
  // Test collections.
631
1
  param.add_map_key()->set_string_value("a");
632
1
  param.add_map_value()->set_int32_value(21);
633
1
  param.add_map_key()->set_string_value("b");
634
1
  param.add_map_value()->set_int32_value(22);
635
1
  check_tojson(param, "{\"a\":21,\"b\":22}");
636
637
1
  param.add_map_key()->set_string_value("c");
638
1
  param.add_map_value()->set_string_value("23");
639
1
  check_tojson(param, "{\"a\":21,\"b\":22,\"c\":\"23\"}");
640
641
1
  param.add_map_key()->set_int32_value(88);
642
1
  param.add_map_value()->set_string_value("24");
643
1
  check_tojson(param, "{\"88\":\"24\",\"a\":21,\"b\":22,\"c\":\"23\"}");
644
645
1
  param.add_map_key()->set_int32_value(99);
646
1
  param.add_map_value()->set_int32_value(25);
647
1
  check_tojson(param, "{\"88\":\"24\",\"99\":25,\"a\":21,\"b\":22,\"c\":\"23\"}");
648
649
1
  param.add_set_elem()->set_string_value("a");
650
1
  param.add_set_elem()->set_string_value("b");
651
1
  check_tojson(param, "[\"a\",\"b\"]");
652
653
1
  param.add_list_elem()->set_int32_value(123);
654
1
  param.add_list_elem()->set_int32_value(456);
655
1
  check_tojson(param, "[123,456]");
656
657
  // Test FROZEN<SET> or FROZEN<LIST>.
658
1
  param.add_frozen_elem()->set_int32_value(789);
659
1
  param.add_frozen_elem()->set_int32_value(123);
660
1
  check_tojson(param, "[789,123]");
661
1
}
662
663
} // namespace bfql
664
} // namespace yb