diff --git a/CHANGELOG.md b/CHANGELOG.md index de914232..552edd30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -307,6 +307,7 @@ Version 3.4.0 - 2026 ??? - Fix Database::isUnencrypted() to compare the full 16-byte header in binary mode (#553) - Fix execute_many() to clear stale bindings between parameter sets (#554) - Fix Column::operator<< to stream the exact column bytes via getString() (#556) +- Fix large `std::string` binding sizes (#563) - Restore the Coverity Scan static analysis as a GitHub Actions workflow, replacing the old Travis CI job - Fix Database::getHeaderInfo() signed-shift UB and use fixed-width types for the Header struct (#558) - Fix Savepoint destructor to catch all exceptions and track rollback state to avoid std::terminate (#559) diff --git a/README.md b/README.md index e05a43e1..db9b5479 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,8 @@ And the following IDEs/Compilers - a modern C++11 STL implementation with GCC, Clang, or Visual Studio 2015 - exception support (the class Exception inherits from std::runtime_error) -- the SQLite library (3.7.15 minimum from 2012-12-12) either by linking to it dynamically or statically (install the libsqlite3-dev package under Debian/Ubuntu/Mint Linux), +- the SQLite library (3.8.7 minimum from 2014-10-17), either by linking to it dynamically or statically + (install the libsqlite3-dev package under Debian/Ubuntu/Mint Linux), or by adding its source file in your project code base (source code provided in src/sqlite3 for Windows), with the `SQLITE_ENABLE_COLUMN_METADATA` macro defined (see http://www.sqlite.org/compile.html#enable_column_metadata). diff --git a/src/Statement.cpp b/src/Statement.cpp index 88f2ca01..69f3ec8b 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -111,8 +111,8 @@ void Statement::bind(const int aIndex, const double aValue) // Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const std::string& aValue) { - const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, aValue.c_str(), - static_cast(aValue.size()), SQLITE_TRANSIENT); + const int ret = sqlite3_bind_text64(getPreparedStatement(), aIndex, aValue.c_str(), + static_cast(aValue.size()), SQLITE_TRANSIENT, SQLITE_UTF8); check(ret); } @@ -133,8 +133,8 @@ void Statement::bind(const int aIndex, const void* apValue, const int aSize) // Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bindNoCopy(const int aIndex, const std::string& aValue) { - const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, aValue.c_str(), - static_cast(aValue.size()), SQLITE_STATIC); + const int ret = sqlite3_bind_text64(getPreparedStatement(), aIndex, aValue.c_str(), + static_cast(aValue.size()), SQLITE_STATIC, SQLITE_UTF8); check(ret); } diff --git a/tests/Statement_test.cpp b/tests/Statement_test.cpp index df03c415..94f1376d 100644 --- a/tests/Statement_test.cpp +++ b/tests/Statement_test.cpp @@ -331,7 +331,7 @@ TEST(Statement, bindings) // Fourth row with string/int64/float { - const std::string fourth("fourth"); + const std::string fourth("fou\0rth", sizeof("fou\0rth") - 1); const int64_t int64 = 12345678900000LL; const float float32 = 0.234f; insert.bind(1, fourth); @@ -345,7 +345,8 @@ TEST(Statement, bindings) EXPECT_TRUE (query.hasRow()); EXPECT_FALSE(query.isDone()); EXPECT_EQ(4, query.getColumn(0).getInt64()); - EXPECT_EQ(fourth, query.getColumn(1).getText()); + EXPECT_EQ(fourth, query.getColumn(1).getString()); + EXPECT_EQ(static_cast(fourth.size()), query.getColumn(1).getBytes()); EXPECT_EQ(12345678900000LL, query.getColumn(2).getInt64()); EXPECT_FLOAT_EQ(0.234f, (float)query.getColumn(3).getDouble()); } @@ -434,7 +435,7 @@ TEST(Statement, bindNoCopy) // Insert one row with all variants of bindNoCopy() { const char* txt1 = "first"; - const std::string txt2 = "sec\0nd"; + const std::string txt2("sec\0nd", sizeof("sec\0nd") - 1); const char blob[] = {'b','l','\0','b'}; insert.bindNoCopy(1, txt1); insert.bindNoCopy(2, txt2); @@ -448,7 +449,8 @@ TEST(Statement, bindNoCopy) EXPECT_FALSE(query.isDone()); EXPECT_EQ(1, query.getColumn(0).getInt64()); EXPECT_STREQ(txt1, query.getColumn(1).getText()); - EXPECT_EQ(0, memcmp(&txt2[0], &query.getColumn(2).getString()[0], txt2.size())); + EXPECT_EQ(txt2, query.getColumn(2).getString()); + EXPECT_EQ(static_cast(txt2.size()), query.getColumn(2).getBytes()); EXPECT_EQ(0, memcmp(blob, &query.getColumn(3).getString()[0], sizeof(blob))); } } @@ -694,7 +696,7 @@ TEST(Statement, bindNoCopyByName) // Insert one row with all variants of bindNoCopy() { const char* txt1 = "first"; - const std::string txt2 = "sec\0nd"; + const std::string txt2("sec\0nd", sizeof("sec\0nd") - 1); const char blob[] = { 'b','l','\0','b' }; insert.bindNoCopy("@txt1", txt1); insert.bindNoCopy("@txt2", txt2); @@ -709,7 +711,8 @@ TEST(Statement, bindNoCopyByName) EXPECT_FALSE(query.isDone()); EXPECT_EQ(1, query.getColumn(0).getInt64()); EXPECT_STREQ(txt1, query.getColumn(1).getText()); - EXPECT_EQ(0, memcmp(&txt2[0], &query.getColumn(2).getString()[0], txt2.size())); + EXPECT_EQ(txt2, query.getColumn(2).getString()); + EXPECT_EQ(static_cast(txt2.size()), query.getColumn(2).getBytes()); EXPECT_EQ(0, memcmp(blob, &query.getColumn(3).getString()[0], sizeof(blob))); } @@ -722,7 +725,7 @@ TEST(Statement, bindNoCopyByName) const std::string atxt2 = "@txt2"; const std::string ablob = "@blob"; const char* txt1 = "first2"; - const std::string txt2 = "sec\0nd2"; + const std::string txt2("sec\0nd2", sizeof("sec\0nd2") - 1); const char blob[] = { 'b','l','\0','b','2' }; insert.bindNoCopy(atxt1, txt1); insert.bindNoCopy(atxt2, txt2); @@ -738,7 +741,8 @@ TEST(Statement, bindNoCopyByName) EXPECT_FALSE(query.isDone()); EXPECT_EQ(2, query.getColumn(0).getInt64()); EXPECT_STREQ(txt1, query.getColumn(1).getText()); - EXPECT_EQ(0, memcmp(&txt2[0], &query.getColumn(2).getString()[0], txt2.size())); + EXPECT_EQ(txt2, query.getColumn(2).getString()); + EXPECT_EQ(static_cast(txt2.size()), query.getColumn(2).getBytes()); EXPECT_EQ(0, memcmp(blob, &query.getColumn(3).getString()[0], sizeof(blob))); } }