Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions include/SQLiteCpp/Column.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

#include <string>
#include <memory>
#if __cplusplus >= 201703L // c++17
#include <string_view>
#endif

// Forward declarations to avoid inclusion of <sqlite3.h> in a header
struct sqlite3_stmt;
Expand Down Expand Up @@ -102,6 +105,24 @@ class SQLITECPP_API Column
* Note this correctly handles strings that contain null bytes.
*/
std::string getString() const;
#if __cplusplus >= 201703L
/**
* @brief Return a std::string_view for a TEXT or BLOB column.
*
* Note this correctly handles strings that contain null bytes.
*
* @warning returned string_view is only valid until there is a type
* conversion or the statement is stepped or reset.
*/
std::string_view getStringView() const {
// See getString implementation
(void)getBytes();
auto data = static_cast<const char*>(getBlob());
if (data == nullptr)
return {};
return std::string_view(data, getBytes());
}
#endif // c++17

/**
* @brief Return the type of the value of the column using sqlite3_column_type()
Expand Down Expand Up @@ -227,6 +248,21 @@ class SQLITECPP_API Column
return getString();
}

#if __cplusplus >= 201703L

/**
* @brief Inline cast operator to std::string_view
*
* Handles BLOB or TEXT, which may contain null bytes within
*
* @see getStringView
*/
operator std::string_view() const
{
return getStringView();
}
#endif // c++17

private:
Statement::TStatementPtr mStmtPtr; ///< Shared Pointer to the prepared SQLite Statement Object
int mIndex; ///< Index of the column in the row of result, starting at 0
Expand Down
92 changes: 92 additions & 0 deletions include/SQLiteCpp/Statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include <string>
#include <map>
#include <memory>
#if __cplusplus >= 201703L
#include <string_view>
#endif

// Forward declarations to avoid inclusion of <sqlite3.h> in a header
struct sqlite3;
Expand Down Expand Up @@ -152,6 +155,18 @@ class SQLITECPP_API Statement
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
*/
void bind(const int aIndex, const char* apValue);
#if __cplusplus >= 201703L
/**
* @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
*/
void bind(const int aIndex, const std::string_view aValue)
{
bindText64(aIndex, aValue.data(), aValue.size());
}
#endif // c++17

/**
* @brief Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand Down Expand Up @@ -185,6 +200,19 @@ class SQLITECPP_API Statement
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
*/
void bindNoCopy(const int aIndex, const char* apValue);
#if __cplusplus >= 201703L
/**
* @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1).
*
* The string can contain null characters as it is bound using its size.
*
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
*/
void bindNoCopy(const int aIndex, const std::string_view aValue)
{
bindTextNoCopy64(aIndex, aValue.data(), aValue.size());
}
#endif // c++17
/**
* @brief Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand Down Expand Up @@ -252,6 +280,17 @@ class SQLITECPP_API Statement
{
bind(getIndex(apName), aValue);
}
#if __cplusplus >= 201703L
/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
*/
void bind(const char* apName, const std::string_view aValue)
{
bind(getIndex(apName), aValue);
}
#endif // c++17
/**
* @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand Down Expand Up @@ -295,6 +334,19 @@ class SQLITECPP_API Statement
{
bindNoCopy(getIndex(apName), aValue);
}
#if __cplusplus >= 201703L
/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* The string can contain null characters as it is binded using its size.
*
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
*/
void bindNoCopy(const char* apName, const std::string_view aValue)
{
bindNoCopy(getIndex(apName), aValue);
}
#endif
/**
* @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand Down Expand Up @@ -382,6 +434,17 @@ class SQLITECPP_API Statement
{
bind(aName.c_str(), aValue);
}
#if __cplusplus >= 201703L
/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
*/
void bind(const std::string& aName, const std::string_view aValue )
{
bind(aName.c_str(), aValue);
}
#endif
/**
* @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand Down Expand Up @@ -425,6 +488,19 @@ class SQLITECPP_API Statement
{
bindNoCopy(aName.c_str(), aValue);
}
#if __cplusplus >= 201703L
/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* The string can contain null characters as it is binded using its size.
*
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
*/
void bindNoCopy(const std::string& aName, const std::string_view aValue)
{
bindNoCopy(aName.c_str(), aValue);
}
#endif // c++17
/**
* @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand Down Expand Up @@ -860,6 +936,22 @@ class SQLITECPP_API Statement
*/
sqlite3_stmt* getPreparedStatement() const;

/**
* @brief Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
*/
void bindText64(const int aIndex, const char* apValue, const uint64_t aSize);

/**
* @brief Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* The text can contain null characters as it is bound using its size
*
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
*/
void bindTextNoCopy64(const int aIndex, const char* apValue, const uint64_t aSize);

std::string mQuery; //!< UTF-8 SQL Query
sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle
TStatementPtr mpPreparedStatement; //!< Shared Pointer to the prepared SQLite Statement Object
Expand Down
16 changes: 16 additions & 0 deletions src/Statement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,4 +444,20 @@ sqlite3_stmt* Statement::getPreparedStatement() const
throw SQLite::Exception("Statement was not prepared.");
}

// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bindText64(const int aIndex, const char* apValue, const uint64_t aSize)
{
const int ret = sqlite3_bind_text64(getPreparedStatement(), aIndex, apValue,
static_cast<sqlite3_uint64>(aSize), SQLITE_TRANSIENT, SQLITE_UTF8);
check(ret);
}

// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bindTextNoCopy64(const int aIndex, const char* apValue, const uint64_t aSize)
{
const int ret = sqlite3_bind_text64(getPreparedStatement(), aIndex, apValue,
static_cast<sqlite3_uint64>(aSize), SQLITE_STATIC, SQLITE_UTF8);
check(ret);
}

} // namespace SQLite
38 changes: 28 additions & 10 deletions tests/Column_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ static void test_column_basis(bool utf16)
const unsigned short uint4 = query.getColumn(0); // operator unsigned short()
const char* ptxt = query.getColumn(1); // operator const char*()
const std::string msg = query.getColumn(1); // operator std::string() (or const char* with MSVC)
#if __cplusplus >= 201703L
const std::string_view sv = query.getColumn(1); // operator std::string() (or const char* with MSVC)
#endif
const int integer = query.getColumn(2); // operator int()
const double real = query.getColumn(3); // operator double()
const void* pblob = query.getColumn(4); // operator void*()
Expand All @@ -97,6 +100,9 @@ static void test_column_basis(bool utf16)
EXPECT_EQ(1U, uint4);
EXPECT_STREQ("first", ptxt);
EXPECT_EQ("first", msg);
#if __cplusplus >= 201703L
EXPECT_EQ("first", sv);
#endif
EXPECT_EQ(-123, integer);
EXPECT_DOUBLE_EQ(0.123, real);
EXPECT_EQ(0, memcmp("bl\0b", pblob, size));
Expand All @@ -112,26 +118,38 @@ static void test_column_basis(bool utf16)

// validates every variant of explicit getters
{
int64_t id = query.getColumn(0).getInt64();
const unsigned int uint1 = query.getColumn(0).getUInt();
const uint32_t uint2 = query.getColumn(0).getUInt();
const std::string msg1 = query.getColumn(1).getString();
const char* ptxt = query.getColumn(1).getText();
const std::string msg2 = query.getColumn(1).getText();
const int integer = query.getColumn(2).getInt();
const double real = query.getColumn(3).getDouble();
const void* pblob = query.getColumn(4).getBlob();
const std::string sblob = query.getColumn(4).getString();
int64_t id = query.getColumn(0).getInt64();
const unsigned int uint1 = query.getColumn(0).getUInt();
const uint32_t uint2 = query.getColumn(0).getUInt();
const std::string msg1 = query.getColumn(1).getString();
const char* ptxt = query.getColumn(1).getText();
const std::string msg2 = query.getColumn(1).getText();
#if __cplusplus >= 201703L
const std::string_view sv = query.getColumn(1).getStringView();
#endif
const int integer = query.getColumn(2).getInt();
const double real = query.getColumn(3).getDouble();
const void* pblob = query.getColumn(4).getBlob();
const std::string sblob = query.getColumn(4).getString();
#if __cplusplus >= 201703L
const std::string_view svblob = query.getColumn(4).getStringView();
#endif
EXPECT_EQ(1, id);
EXPECT_EQ(1U, uint1);
EXPECT_EQ(1U, uint2);
EXPECT_STREQ("first", ptxt);
EXPECT_EQ("first", msg1);
EXPECT_EQ("first", msg2);
#if __cplusplus >= 201703L
EXPECT_EQ("first", sv);
#endif
EXPECT_EQ(-123, integer);
EXPECT_DOUBLE_EQ(0.123, real);
EXPECT_EQ(0, memcmp("bl\0b", pblob, 4));
EXPECT_EQ(0, memcmp("bl\0b", &sblob[0], 4));
#if __cplusplus >= 201703L
EXPECT_EQ(0, memcmp("bl\0b", svblob.data(), 4));
#endif
}

// Validate getBytes(), getType(), isInteger(), isNull()...
Expand Down
46 changes: 46 additions & 0 deletions tests/Statement_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1241,3 +1241,49 @@ TEST(Statement, getChanges)
EXPECT_EQ(2, update.exec());
EXPECT_EQ(2, update.getChanges());
}

// Test support for std::string_view which requries C++17
#if __cplusplus >= 201703L
TEST(Statement, stringViewSupport)
{
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
EXPECT_EQ(SQLite::OK, db.getErrorCode());

// Create a new table
EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, txt1 TEXT, txt2 TEXT, txt3 TEXT, txt4 TEXT)"));
EXPECT_EQ(SQLite::OK, db.getErrorCode());

// Insertion with bindable parameters
SQLite::Statement insert(db, "INSERT INTO test VALUES (NULL, ?, ?, ?, ?)");

// Compile a SQL query to check the results
SQLite::Statement query(db, "SELECT * FROM test");
EXPECT_STREQ("SELECT * FROM test", query.getQuery().c_str());
EXPECT_EQ(5, query.getColumnCount());

// Insert one row with all string_view variants of bind/bindNoCopy
{
const char* text = "123\0test";
const std::string_view sv1{text, 8};
std::string_view sv2{"abcd"};
insert.bindNoCopy(1, sv1);
insert.bindNoCopy(2, sv2);
insert.bind(3, sv1);
insert.bind(4, sv2);
EXPECT_EQ(1, insert.exec());
EXPECT_EQ(SQLITE_DONE, db.getErrorCode());

// Check the result
query.executeStep();
EXPECT_TRUE(query.hasRow());
EXPECT_FALSE(query.isDone());
EXPECT_EQ(1, query.getColumn(0).getInt64());

EXPECT_EQ(sv1, query.getColumn(1).getStringView());
EXPECT_EQ(sv2, query.getColumn(2).getStringView());
EXPECT_EQ(sv1, query.getColumn(3).getStringView());
EXPECT_EQ(sv2, query.getColumn(4).getStringView());
}
}
#endif // c++17
Loading