Skip to content
This repository was archived by the owner on Sep 27, 2019. It is now read-only.

Commit 9592332

Browse files
committed
Beefed up tests
1 parent bd4c2a7 commit 9592332

File tree

3 files changed

+107
-42
lines changed

3 files changed

+107
-42
lines changed

src/function/numeric_functions.cpp

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -182,24 +182,6 @@ type::Value NumericFunctions::_Round(const std::vector<type::Value> &args) {
182182

183183
namespace {
184184

185-
/**
186-
* Skip all leading and trailing whitespace from the string bounded by the
187-
* provided pointers. This function will modify the input pointers to point to
188-
* the first non-whitespace space character at the start and end of the input
189-
* string.
190-
*
191-
* @param[in,out] left Pointer to the left-most character in the input string
192-
* @param[in,out] right Pointer to the right-most character in the input string
193-
*/
194-
void TrimLeftRight(const char *&left, const char *&right) {
195-
while (*left == ' ') {
196-
left++;
197-
}
198-
while (right > left && *(right - 1) == ' ') {
199-
right--;
200-
}
201-
}
202-
203185
/**
204186
* Convert the provided input string into an integral number. This function
205187
* handles leading whitespace and leading negative (-) or positive (+) signs.
@@ -216,16 +198,13 @@ T ParseInteger(const char *ptr, uint32_t len) {
216198
static_assert(std::is_integral<T>::value,
217199
"Must provide integer-type when calling ParseInteger");
218200

219-
if (len == 0) {
220-
codegen::RuntimeFunctions::ThrowInvalidInputStringException();
221-
__builtin_unreachable();
222-
}
223-
224201
const char *start = ptr;
225202
const char *end = start + len;
226203

227-
// Trim leading and trailing whitespace
228-
TrimLeftRight(start, end);
204+
// Trim leading whitespace
205+
while (start < end && *start == ' ') {
206+
start++;
207+
}
229208

230209
// Check negative or positive sign
231210
bool negative = false;
@@ -238,18 +217,26 @@ T ParseInteger(const char *ptr, uint32_t len) {
238217

239218
// Convert
240219
int64_t num = 0;
241-
while (start != end) {
220+
while (start < end) {
242221
if (*start < '0' || *start > '9') {
243-
codegen::RuntimeFunctions::ThrowInvalidInputStringException();
244-
__builtin_unreachable();
222+
break;
245223
}
246224

247225
num = (num * 10) + (*start - '0');
248226

249227
start++;
250228
}
251229

252-
PELOTON_ASSERT(start == end);
230+
// Trim trailing whitespace
231+
while (start < end && *start == ' ') {
232+
start++;
233+
}
234+
235+
// If we haven't consumed everything at this point, it was an invalid input
236+
if (start < end) {
237+
codegen::RuntimeFunctions::ThrowInvalidInputStringException();
238+
__builtin_unreachable();
239+
}
253240

254241
// Negate number if we need to
255242
if (negative) {
@@ -279,10 +266,13 @@ bool NumericFunctions::InputBoolean(
279266
__builtin_unreachable();
280267
}
281268

282-
const char *start = ptr, *end = ptr + len;
269+
const char *start = ptr;
270+
const char *end = ptr + len;
283271

284-
// Trim leading and trailing whitespace
285-
TrimLeftRight(start, end);
272+
// Trim leading whitespace
273+
while (start < end && *start == ' ') {
274+
start++;
275+
}
286276

287277
//
288278
uint64_t trimmed_len = end - start;
@@ -393,13 +383,16 @@ double NumericFunctions::InputDecimal(
393383
__builtin_unreachable();
394384
}
395385

386+
const char *start = ptr;
387+
const char *end = ptr + len;
388+
396389
// We don't trim because std::strtod() does the trimming for us
397390

398391
// TODO(pmenon): Optimize me later
399-
char *end = nullptr;
400-
double ret = std::strtod(ptr, &end);
392+
char *consumed_ptr = nullptr;
393+
double ret = std::strtod(ptr, &consumed_ptr);
401394

402-
if (unlikely_branch(end == ptr)) {
395+
if (unlikely_branch(consumed_ptr == start)) {
403396
if (errno == ERANGE) {
404397
codegen::RuntimeFunctions::ThrowOverflowException();
405398
__builtin_unreachable();
@@ -409,6 +402,17 @@ double NumericFunctions::InputDecimal(
409402
}
410403
}
411404

405+
// Eat the rest
406+
while (consumed_ptr < end && *consumed_ptr == ' ') {
407+
consumed_ptr++;
408+
}
409+
410+
// If we haven't consumed everything at this point, it was an invalid input
411+
if (consumed_ptr < end) {
412+
codegen::RuntimeFunctions::ThrowInvalidInputStringException();
413+
__builtin_unreachable();
414+
}
415+
412416
// Done
413417
return ret;
414418
}

src/include/index/bwtree.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7585,7 +7585,7 @@ class BwTree : public BwTreeBase {
75857585
// would always fail, until we have cleaned all epoch nodes
75867586
current_epoch_p = nullptr;
75877587

7588-
LOG_TRACE("Clearing the epoch in ~EpochManager()...");
7588+
LOG_TRACE("Clearing the epoch in ~EpochManager() ...");
75897589

75907590
// If all threads has exited then all thread counts are
75917591
// 0, and therefore this should proceed way to the end

test/codegen/value_integrity_test.cpp

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//
77
// Identification: test/codegen/value_integrity_test.cpp
88
//
9-
// Copyright (c) 2015-17, Carnegie Mellon University Database Group
9+
// Copyright (c) 2015-2018, Carnegie Mellon University Database Group
1010
//
1111
//===----------------------------------------------------------------------===//
1212

@@ -190,8 +190,9 @@ void TestInputIntegral(
190190
extra_valid_tests.end());
191191

192192
// Default invalid tests
193-
std::vector<std::string> invalid_tests = {"a", "-b", "+c", " 1c",
194-
"2d ", "3 3", "-4 4"};
193+
std::vector<std::string> invalid_tests = {"a", "-b", "+c", " 1c",
194+
"2d ", "3 3", "-4 4", "-5 a ",
195+
" -6 a", " c 7 "};
195196
invalid_tests.insert(invalid_tests.end(), extra_invalid_tests.begin(),
196197
extra_invalid_tests.end());
197198

@@ -205,19 +206,25 @@ void TestInputIntegral(
205206
for (const auto &test : valid_tests) {
206207
auto *ptr = test.first.data();
207208
auto len = static_cast<uint32_t>(test.first.length());
208-
EXPECT_EQ(test.second, TestFunc(type, ptr, len));
209+
try {
210+
EXPECT_EQ(test.second, TestFunc(type, ptr, len));
211+
} catch (std::exception &e) {
212+
EXPECT_TRUE(false) << "Valid input '" << test.first << "' threw an error";
213+
}
209214
}
210215

211216
for (const auto &test : invalid_tests) {
212217
auto *ptr = test.data();
213218
auto len = static_cast<uint32_t>(test.length());
214-
EXPECT_THROW(TestFunc(type, ptr, len), std::runtime_error);
219+
EXPECT_THROW(TestFunc(type, ptr, len), std::runtime_error)
220+
<< "Input '" << test << "' was expected to throw an error, but did not";
215221
}
216222

217223
for (const auto &test : overflow_tests) {
218224
auto *ptr = test.data();
219225
auto len = static_cast<uint32_t>(test.length());
220-
EXPECT_THROW(TestFunc(type, ptr, len), std::overflow_error);
226+
EXPECT_THROW(TestFunc(type, ptr, len), std::overflow_error)
227+
<< "Input '" << test << "' expected to overflow, but did not";
221228
}
222229
}
223230
} // namespace
@@ -238,5 +245,59 @@ TEST_F(ValueIntegrityTest, InputIntegralTypesTest) {
238245
TestInputIntegral<int64_t>(bigint, function::NumericFunctions::InputBigInt);
239246
}
240247

248+
TEST_F(ValueIntegrityTest, InputDecimalTypesTest) {
249+
codegen::type::Type decimal{type::TypeId::DECIMAL, false};
250+
251+
// First check some valid cases
252+
std::vector<std::pair<std::string, double>> valid_tests = {
253+
{"0.0", 0.0},
254+
{"-1.0", -1.0},
255+
{"2.0", 2.0},
256+
{"+3.0", 3.0},
257+
{" 4.0", 4.0},
258+
{" -5.0", -5.0},
259+
{" +6.0", 6.0},
260+
{"7.0 ", 7.0},
261+
{"-8.0 ", -8.0},
262+
{" 9.0 ", 9.0},
263+
{" -10.0 ", -10.0},
264+
{" +11.0 ", 11.0}};
265+
266+
for (const auto &test_case : valid_tests) {
267+
auto *ptr = test_case.first.data();
268+
auto len = static_cast<uint32_t>(test_case.first.length());
269+
EXPECT_EQ(test_case.second,
270+
function::NumericFunctions::InputDecimal(decimal, ptr, len));
271+
}
272+
273+
// Now let's try some invalid ones. Take each valid test and randomly insert
274+
// a character somewhere.
275+
std::vector<std::string> invalid_tests;
276+
277+
std::random_device rd;
278+
std::mt19937 rng(rd());
279+
280+
for (const auto &valid_test : valid_tests) {
281+
auto orig = valid_test.first;
282+
283+
std::uniform_int_distribution<> dist(0, orig.length());
284+
auto pos = dist(rng);
285+
286+
auto invalid_num = orig.substr(0, pos) + "aa" + orig.substr(pos);
287+
288+
invalid_tests.push_back(invalid_num);
289+
}
290+
291+
// Now check that each test throws an invalid string error
292+
for (const auto &invalid_test : invalid_tests) {
293+
auto *ptr = invalid_test.data();
294+
auto len = static_cast<uint32_t>(invalid_test.length());
295+
EXPECT_THROW(function::NumericFunctions::InputDecimal(decimal, ptr, len),
296+
std::runtime_error)
297+
<< "Input '" << invalid_test
298+
<< "' expected to throw error, but passed parsing logic";
299+
}
300+
}
301+
241302
} // namespace test
242303
} // namespace peloton

0 commit comments

Comments
 (0)