diff --git a/src/internal/string_utilities.h b/src/internal/string_utilities.h index 79b87d9..5e97a25 100644 --- a/src/internal/string_utilities.h +++ b/src/internal/string_utilities.h @@ -41,6 +41,5 @@ class REFLECTION_EXPORT StringUtilities { static std::wstring FromUtf8(const char* utf8_string, size_t byte_count); static std::string Join(const std::vector& list, const std::string& separator); - static std::string Join(const std::vector& list, char c); }; } // namespace sqlite_reflection diff --git a/src/queries.cc b/src/queries.cc index 46bd366..9acde85 100644 --- a/src/queries.cc +++ b/src/queries.cc @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -374,10 +375,15 @@ std::wstring FetchRecordsQuery::GetColumnValue(const int col) const { const int col_type = sqlite3_column_type(stmt_, col); switch (col_type) { case SQLITE_INTEGER: - return std::to_wstring(sqlite3_column_int(stmt_, col)); - - case SQLITE_FLOAT: - return std::to_wstring(sqlite3_column_double(stmt_, col)); + return std::to_wstring(sqlite3_column_int64(stmt_, col)); + + case SQLITE_FLOAT: { + // %.17g round-trips any double exactly; to_wstring's fixed 6-decimal + // formatting would silently truncate precision here + char buffer[64]; + std::snprintf(buffer, sizeof(buffer), "%.17g", sqlite3_column_double(stmt_, col)); + return StringUtilities::FromUtf8(buffer, std::strlen(buffer)); + } case SQLITE_TEXT: { const auto content = reinterpret_cast(sqlite3_column_text(stmt_, col)); diff --git a/src/string_utilities.cc b/src/string_utilities.cc index 508568e..9622a1b 100644 --- a/src/string_utilities.cc +++ b/src/string_utilities.cc @@ -24,7 +24,6 @@ #include #include -#include #ifndef _WIN32 #include #endif @@ -86,7 +85,3 @@ std::string StringUtilities::Join(const std::vector& list, const st return std::accumulate(list.begin() + 1, list.end(), list[0], [&](const std::string& a, const std::string& b) { return a + separator + b; }); } - -std::string StringUtilities::Join(const std::vector& list, char c) { - return Join(list, std::string(1, c)); -} diff --git a/tests/database_test.cc b/tests/database_test.cc index ed55737..0c64bfc 100644 --- a/tests/database_test.cc +++ b/tests/database_test.cc @@ -571,6 +571,48 @@ TEST_F(DatabaseTest, FetchWithPredicateChaining) { EXPECT_EQ(37, fetched_persons[1].age); } +TEST_F(DatabaseTest, FetchPreservesInt64ValuesBeyondInt32Range) { + const auto db = Database::Instance(); + + // Values above INT32_MAX must survive a round-trip; sqlite3_column_int would + // truncate/wrap these to 32 bits + const int64_t above_int32_max = 5000000000LL; + const int64_t near_int64_max = 9000000000000000000LL; + + std::vector company; + company.push_back({L"Big Corp", above_int32_max, L"Nowhere", 1.0, 1}); + company.push_back({L"Huge Corp", near_int64_max, L"Nowhere", 1.0, 2}); + + db->Save(company); + + const auto fetched_first = db->Fetch(1); + EXPECT_EQ(above_int32_max, fetched_first.age); + + const auto fetched_second = db->Fetch(2); + EXPECT_EQ(near_int64_max, fetched_second.age); +} + +TEST_F(DatabaseTest, FetchPreservesHighPrecisionDoubleValues) { + const auto db = Database::Instance(); + + // std::to_wstring(double) formats with a fixed 6 decimal places, which would + // silently drop precision here + const double high_precision = 0.12345678901234567; + const double large_magnitude = 123456789012345.67; + + std::vector company; + company.push_back({L"Precision Inc", 1, L"Nowhere", high_precision, 1}); + company.push_back({L"Large Corp", 2, L"Nowhere", large_magnitude, 2}); + + db->Save(company); + + const auto fetched_first = db->Fetch(1); + EXPECT_DOUBLE_EQ(high_precision, fetched_first.salary); + + const auto fetched_second = db->Fetch(2); + EXPECT_DOUBLE_EQ(large_magnitude, fetched_second.salary); +} + TEST_F(DatabaseTest, RawSqlQueryForPersistedRecord) { const auto db = Database::Instance();