Skip to content

Commit

Permalink
Merge 1a98ec4 into ffc1a3e
Browse files Browse the repository at this point in the history
  • Loading branch information
kaptenhonek committed Jan 20, 2020
2 parents ffc1a3e + 1a98ec4 commit 6c1e676
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 22 deletions.
136 changes: 130 additions & 6 deletions include/SQLiteCpp/Statement.h
Expand Up @@ -171,7 +171,16 @@ class 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);
void bind(const int aIndex, const char* apValue, const int aSize);
/**
* @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 bind(const int aIndex, const char* apValue)
{
bind(aIndex, apValue, -1);
}
/**
* @brief Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand All @@ -186,14 +195,25 @@ class 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 std::string& aValue);
/**
* @brief Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* Main usage is with text which might not be null-terminated.
*
* @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, const int aSize);
/**
* @brief Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* Main usage is with null-terminated literal text (aka in code static strings)
*
* @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);
inline void bindNoCopy(const int aIndex, const char* apValue)
{
bindNoCopy(aIndex, apValue, -1);
}
/**
* @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 @@ -262,14 +282,23 @@ class Statement
{
bind(getIndex(apName), aValue);
}
/**
* @brief Bind a text 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 char* apValue, const int aSize)
{
bind(getIndex(apName), apValue, aSize);
}
/**
* @brief Bind a text 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 char* apValue)
{
bind(getIndex(apName), apValue);
bind(getIndex(apName), apValue, -1);
}
/**
* @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
Expand All @@ -291,6 +320,17 @@ class Statement
{
bindNoCopy(getIndex(apName), aValue);
}
/**
* @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* Main usage is with text which might not be null-terminated
*
* @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 char* apValue, const int aSize)
{
bindNoCopy(getIndex(apName), apValue, aSize);
}
/**
* @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand All @@ -300,7 +340,7 @@ class Statement
*/
void bindNoCopy(const char* apName, const char* apValue)
{
bindNoCopy(getIndex(apName), apValue);
bindNoCopy(getIndex(apName), apValue, -1);
}
/**
* @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
Expand Down Expand Up @@ -384,7 +424,16 @@ class Statement
*/
void bind(const std::string& aName, const char* apValue)
{
bind(aName.c_str(), apValue);
bind(aName.c_str(), apValue, -1);
}
/**
* @brief Bind a text 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
*/
inline void bind(const std::string& aName, const char* apValue, const int aSize)
{
bind(aName.c_str(), apValue, aSize);
}
/**
* @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
Expand Down Expand Up @@ -415,7 +464,18 @@ class Statement
*/
void bindNoCopy(const std::string& aName, const char* apValue)
{
bindNoCopy(aName.c_str(), apValue);
bindNoCopy(aName.c_str(), apValue, -1);
}
/**
* @brief Bind a text value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* Main usage is with null-terminated literal text (aka in code static strings)
*
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
*/
inline void bindNoCopy(const std::string& aName, const char* apValue, const int aSize)
{
bindNoCopy(aName.c_str(), apValue, aSize);
}
/**
* @brief Bind a binary blob value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
Expand All @@ -436,6 +496,70 @@ class Statement
bind(aName.c_str());
}

// Provide bindings for std::string_view if using at least c++17
#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
*/
inline void bind(const int aIndex, std::string_view aValue)
{
bind(aIndex, aValue.data(), static_cast<int>(aValue.size()));
}
/**
* @brief Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* Main usage is with null-terminated literal text (aka in code static strings)
*
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
*/
inline void bindNoCopy(const int aIndex, std::string_view aValue)
{
bindNoCopy(aIndex, aValue.data(), static_cast<int>(aValue.size()));
}
/**
* @brief Bind a string_view 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
*/
inline void bind(const char* apName, std::string_view aValue)
{
bind(apName, aValue.data(), static_cast<int>(aValue.size()));
}
/**
* @brief Bind a string_view 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.
*/
inline void bindNoCopy(const char* apName, std::string_view aValue)
{
bind(apName, aValue.data(), static_cast<int>(aValue.size()));
}
/**
* @brief Bind a string_view 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
*/
inline void bind(const std::string& aName, std::string_view aValue)
{
bind(aName.c_str(), aValue.data(), static_cast<int>(aValue.size()));
}
/**
* @brief Bind a string_view 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.
*/
inline void bindNoCopy(const std::string& aName, std::string_view aValue)
{
bindNoCopy(aName.c_str(), aValue.data(), static_cast<int>(aValue.size()));
}
#endif

////////////////////////////////////////////////////////////////////////////

/**
Expand Down
8 changes: 4 additions & 4 deletions src/Statement.cpp
Expand Up @@ -105,9 +105,9 @@ void Statement::bind(const int aIndex, const std::string& aValue)
}

// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bind(const int aIndex, const char* apValue)
void Statement::bind(const int aIndex, const char* apValue, const int aSize)
{
const int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, -1, SQLITE_TRANSIENT);
const int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, aSize, SQLITE_TRANSIENT);
check(ret);
}

Expand All @@ -127,9 +127,9 @@ void Statement::bindNoCopy(const int aIndex, const std::string& aValue)
}

// Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
void Statement::bindNoCopy(const int aIndex, const char* apValue)
void Statement::bindNoCopy(const int aIndex, const char* apValue, const int aSize)
{
const int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, -1, SQLITE_STATIC);
const int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, aSize, SQLITE_STATIC);
check(ret);
}

Expand Down
64 changes: 52 additions & 12 deletions tests/Statement_test.cpp
Expand Up @@ -341,7 +341,7 @@ TEST(Statement, bindings)

// Fifth row with binary buffer and a null parameter
{
const char buffer[] = "binary";
const unsigned char buffer[] = "binary";
insert.bind(1, buffer, sizeof(buffer));
insert.bind(2);
EXPECT_EQ(1, insert.exec());
Expand All @@ -351,7 +351,7 @@ TEST(Statement, bindings)
EXPECT_TRUE (query.hasRow());
EXPECT_FALSE(query.isDone());
EXPECT_EQ(5, query.getColumn(0).getInt64());
EXPECT_STREQ(buffer, query.getColumn(1).getText());
EXPECT_EQ(0, memcmp(buffer, &query.getColumn(1).getString()[0], sizeof(buffer)));
EXPECT_TRUE (query.isColumnNull(2));
EXPECT_EQ(0, query.getColumn(2).getInt());
EXPECT_EQ(0.234f, query.getColumn(3).getDouble());
Expand Down Expand Up @@ -406,25 +406,26 @@ TEST(Statement, bindNoCopy)
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, binary BLOB)"));
EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, txt1 TEXT, txt2 TEXT, txt3 TEXT, binary BLOB)"));
EXPECT_EQ(SQLite::OK, db.getErrorCode());

// Insertion with bindable parameters
SQLite::Statement insert(db, "INSERT INTO test VALUES (NULL, ?, ?, ?)");
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(4, query.getColumnCount());
EXPECT_EQ(5, query.getColumnCount());

// Insert one row with all variants of bindNoCopy()
{
const char* txt1 = "first";
const std::string txt2 = "sec\0nd";
const char blob[] = {'b','l','\0','b'};
const unsigned char blob[] = {'b','l','\0','b'};
insert.bindNoCopy(1, txt1);
insert.bindNoCopy(2, txt2);
insert.bindNoCopy(3, blob, sizeof(blob));
insert.bindNoCopy(3, txt2.c_str(), static_cast<int>(txt2.size()));
insert.bindNoCopy(4, blob, sizeof(blob));
EXPECT_EQ(1, insert.exec());
EXPECT_EQ(SQLITE_DONE, db.getErrorCode());

Expand All @@ -435,7 +436,8 @@ TEST(Statement, bindNoCopy)
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(0, memcmp(blob, &query.getColumn(3).getString()[0], sizeof(blob)));
EXPECT_EQ(0, memcmp(&txt2[0], &query.getColumn(3).getString()[0], txt2.size()));
EXPECT_EQ(0, memcmp(blob, &query.getColumn(4).getString()[0], sizeof(blob)));
}
}

Expand Down Expand Up @@ -621,7 +623,7 @@ TEST(Statement, bindByNameString)

// Third row with binary buffer and a null parameter
{
const char buffer[] = "binary";
const unsigned char buffer[] = "binary";
insert.bind(amsg, buffer, sizeof(buffer));
insert.bind(aint);
EXPECT_EQ(1, insert.exec());
Expand All @@ -631,7 +633,7 @@ TEST(Statement, bindByNameString)
EXPECT_TRUE(query.hasRow());
EXPECT_FALSE(query.isDone());
EXPECT_EQ(3, query.getColumn(0).getInt64());
EXPECT_STREQ(buffer, query.getColumn(1).getText());
EXPECT_EQ(0, memcmp(buffer, query.getColumn(1).getText(), 6));
EXPECT_TRUE(query.isColumnNull(2));
EXPECT_EQ(0, query.getColumn(2).getInt());
EXPECT_EQ(0.234f, query.getColumn(3).getDouble());
Expand All @@ -657,6 +659,23 @@ TEST(Statement, bindByNameString)
EXPECT_EQ(4294967295U, query.getColumn(2).getUInt());
EXPECT_EQ(12345678900000LL, query.getColumn(4).getInt64());
}

// reset() without clearbindings()
insert.reset();

// Fifth row with string
{
const std::string first = "fir\0st";
insert.bind(amsg, first.c_str(), static_cast<int>(first.size()));
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(first, query.getColumn(1).getString());
}
}

TEST(Statement, bindNoCopyByName)
Expand Down Expand Up @@ -709,9 +728,9 @@ TEST(Statement, bindNoCopyByName)
const std::string ablob = "@blob";
const char* txt1 = "first2";
const std::string txt2 = "sec\0nd2";
const char blob[] = { 'b','l','\0','b','2' };
const unsigned char blob[] = { 'b','l','\0','b','2' };
insert.bindNoCopy(atxt1, txt1);
insert.bindNoCopy(atxt2, txt2);
insert.bindNoCopy(atxt2, txt2.c_str(), static_cast<int>(txt2.size()));
insert.bindNoCopy(ablob, blob, sizeof(blob));
EXPECT_EQ(1, insert.exec());
EXPECT_EQ(2, db.getLastInsertRowid());
Expand All @@ -727,6 +746,27 @@ TEST(Statement, bindNoCopyByName)
EXPECT_EQ(0, memcmp(&txt2[0], &query.getColumn(2).getString()[0], txt2.size()));
EXPECT_EQ(0, memcmp(blob, &query.getColumn(3).getString()[0], sizeof(blob)));
}

insert.reset();
query.reset();

// Insert a third row with some more variants of bindNoCopy() using std::string names
{
const std::string atxt1 = "@txt1";
const std::string txt1 = "fir\0st";
insert.bindNoCopy(atxt1, txt1);
EXPECT_EQ(1, insert.exec());
EXPECT_EQ(3, db.getLastInsertRowid());
EXPECT_EQ(SQLITE_DONE, db.getErrorCode());

// Check the result
query.executeStep(); // pass on the first row
query.executeStep(); // pass on the second row as well
query.executeStep();
EXPECT_TRUE(query.hasRow());
EXPECT_FALSE(query.isDone());
EXPECT_EQ(txt1, query.getColumn(1).getString());
}
}

TEST(Statement, isColumnNull)
Expand Down

0 comments on commit 6c1e676

Please sign in to comment.