From d9854d81fa63e181685828f7feebcc665177a07e Mon Sep 17 00:00:00 2001 From: eelke Date: Sun, 17 Dec 2017 09:06:18 +0100 Subject: [PATCH] Improved error checking in Value + more to array conversion control. --- pgsql/Pgsql_Value.cpp | 34 ++++++++++++--- pgsql/Pgsql_Value.h | 78 +++++++++++++++++++++++++++++----- tests/PgsqlTests/tst_Value.cpp | 59 +++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 18 deletions(-) diff --git a/pgsql/Pgsql_Value.cpp b/pgsql/Pgsql_Value.cpp index 2bb88d6..ac9bc54 100644 --- a/pgsql/Pgsql_Value.cpp +++ b/pgsql/Pgsql_Value.cpp @@ -45,24 +45,44 @@ Value::operator std::string() const return m_val; } -Value::operator short() const +Value::operator int16_t() const { - return (short)std::atoi(m_val); + int32_t r = operator int32_t(); + if (r <= std::numeric_limits::max() + && r >= std::numeric_limits::min()) + return int16_t(r); + + throw std::runtime_error("Value conversion to int16_t failed (out of range)"); } -Value::operator int() const +Value::operator int32_t() const { - return std::atoi(m_val); + const int len = std::strlen(m_val); + if (len > 0) { + char *endptr = nullptr; + long result = std::strtol(m_val, &endptr, 10); + if (endptr == m_val + len) + return result; + } + throw std::runtime_error("Value conversion to int32_t failed"); } Value::operator Oid() const { - return operator int(); + return operator int32_t(); } -Value::operator long long() const +Value::operator int64_t() const { - return std::strtoull(m_val, nullptr, 10); + const int len = std::strlen(m_val); + if (len > 0) { + char *endptr = nullptr; + int64_t result = std::strtoll(m_val, &endptr, 10); + if (endptr == m_val + len) + return result; + } + throw std::runtime_error("Value conversion to int64_t failed"); +// return std::strtoll(m_val, nullptr, 10); } Value::operator bool() const diff --git a/pgsql/Pgsql_Value.h b/pgsql/Pgsql_Value.h index b5b92e3..3e4daaa 100644 --- a/pgsql/Pgsql_Value.h +++ b/pgsql/Pgsql_Value.h @@ -23,35 +23,91 @@ namespace Pgsql { operator QDate() const; operator QTime() const; operator std::string() const; - operator short() const; - operator int() const; + operator int16_t() const; + operator int32_t() const; operator Oid() const; - operator long long() const; + operator int64_t() const; operator bool() const; operator float() const; operator double() const; bool isString() const; + enum class NullHandling { + Ignore, + Throw + }; /** * * \param insert_iter An insert_iterator which is used to add the elements of the array to a container. */ template - void getAsArray(I insert_iter) const + void getAsArray(I insert_iter, NullHandling nullhandling = NullHandling::Throw) const { using value_type = E; ArrayParser parser(m_val); for (;;) { auto res = parser.GetNextElem(); if (res.ok) { - std::string str(res.value->data(), res.value->length()); -// std::istringstream iss(str); - Value val(str.c_str(), ANYOID); - value_type v; -// iss >> v; - v << val; - insert_iter = v; + if (res.value) { + std::string str(res.value->data(), res.value->length()); + Value val(str.c_str(), ANYOID); + value_type v; + v << val; + insert_iter = v; + } + else { + if (nullhandling == NullHandling::Throw) + throw std::runtime_error("Unexpected NULL value in array"); + } + } + else + break; + } + } + + template + void getAsArray(I insert_iter, const E &value_for_nulls) const + { + using value_type = E; + ArrayParser parser(m_val); + for (;;) { + auto res = parser.GetNextElem(); + if (res.ok) { + if (res.value) { + std::string str(res.value->data(), res.value->length()); + Value val(str.c_str(), ANYOID); + value_type v; + v << val; + insert_iter = v; + } + else { + insert_iter = value_for_nulls; + } + } + else + break; + } + } + + template + void getAsArrayOfOptional(I insert_iter) const + { + using value_type = E; + ArrayParser parser(m_val); + for (;;) { + auto res = parser.GetNextElem(); + if (res.ok) { + if (res.value) { + std::string str(res.value->data(), res.value->length()); + Value val(str.c_str(), ANYOID); + value_type v; + v << val; + insert_iter = v; + } + else { + insert_iter = std::nullopt; + } } else break; diff --git a/tests/PgsqlTests/tst_Value.cpp b/tests/PgsqlTests/tst_Value.cpp index 64b1ffe..5d68f25 100644 --- a/tests/PgsqlTests/tst_Value.cpp +++ b/tests/PgsqlTests/tst_Value.cpp @@ -87,3 +87,62 @@ TEST(Pgsql_Value, getAsArray_QDateTime) ASSERT_EQ(r.size(), 2); } + +TEST(Pgsql_Value, getAsArray_throws_on_NULL) +{ + Pgsql::Value v("{1,NULL,2}", TEXTARRAYOID); + std::vector r; + try { + v.getAsArray(std::back_inserter(r)); + FAIL(); + } catch (std::runtime_error &) { + SUCCEED(); + } catch (...) { + FAIL(); + } +} + +TEST(Pgsql_Value, getAsArray_default_on_NULL) +{ + Pgsql::Value v("{1,NULL,2}", TEXTARRAYOID); + std::vector r; + try { + v.getAsArray(std::back_inserter(r), -1); + ASSERT_EQ(r.size(), 3); + ASSERT_EQ(r[0], 1); + ASSERT_EQ(r[1], -1); + ASSERT_EQ(r[2], 2); + } catch (...) { + FAIL(); + } +} + +TEST(Pgsql_Value, getAsArray_ignore_NULL) +{ + Pgsql::Value v("{1,NULL,2}", TEXTARRAYOID); + std::vector r; + try { + v.getAsArray(std::back_inserter(r), Value::NullHandling::Ignore); + ASSERT_EQ(r.size(), 2); + ASSERT_EQ(r[0], 1); + ASSERT_EQ(r[1], 2); + } catch (...) { + FAIL(); + } +} + +TEST(Pgsql_Value, getAsArrayOptional) +{ + Pgsql::Value v("{1,NULL,2}", TEXTARRAYOID); + std::vector> r; + try { + v.getAsArrayOfOptional(std::back_inserter(r)); + ASSERT_EQ(r.size(), 3); + ASSERT_EQ(r[0], 1); + ASSERT_EQ(r[1], std::nullopt); + ASSERT_EQ(r[2], 2); + } catch (...) { + FAIL(); + } +} +