Skip to content
Merged
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ set(SQLITECPP_TESTS
tests/Database_test.cpp
tests/Statement_test.cpp
tests/Backup_test.cpp
tests/VariadicBind_test.cpp
)
source_group(tests FILES ${SQLITECPP_TESTS})

Expand Down
2 changes: 1 addition & 1 deletion Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -1935,7 +1935,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.

PREDEFINED =
PREDEFINED = __cplusplus=201402L

# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
Expand Down
40 changes: 40 additions & 0 deletions examples/example1/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include <string>

#include <SQLiteCpp/SQLiteCpp.h>
#include <SQLiteCpp/VariadicBind.h>


#ifdef SQLITECPP_ENABLE_ASSERT_HANDLER
namespace SQLite
Expand Down Expand Up @@ -80,6 +82,34 @@ class Example
SQLite::Statement mQuery; ///< Database prepared SQL query
};

void demonstrateVariadicBind() {
#if ( __cplusplus>= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) )
// Open a database file in create/write mode
SQLite::Database db(":memory:", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);

db.exec("DROP TABLE IF EXISTS test");
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");

{
SQLite::Statement query(db, "INSERT INTO test VALUES (?, ?)");

SQLite::bind(query, 42, "fortytwo");
// Execute the one-step query to insert the blob
int nb = query.exec();
std::cout << "INSERT INTO test VALUES (NULL, ?)\", returned " << nb
<< std::endl;
}

SQLite::Statement query(db, "SELECT * FROM test");
std::cout << "SELECT * FROM test :\n";
if (query.executeStep()) {
std::cout << query.getColumn(0).getInt() << "\t\""
<< query.getColumn(1).getText() << "\"\n";
}
#else
throw std::runtime_error("demonstrateVariadicBind(): sorry, no c++14 support in this build.");
#endif
}

int main ()
{
Expand Down Expand Up @@ -422,6 +452,16 @@ int main ()
}
remove("out.png");

//example with variadic bind (requires c++14)
#if ( __cplusplus>= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) )
try {
demonstrateVariadicBind();
} catch (std::exception& e) {
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
#endif

std::cout << "everything ok, quitting\n";

return EXIT_SUCCESS;
Expand Down
78 changes: 78 additions & 0 deletions include/SQLiteCpp/VariadicBind.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* @file VariadicBind.h
* @ingroup SQLiteCpp
* @brief Convenience function for Statement::bind(...)
*
* Copyright (c) 2016 Paul Dreik (github@pauldreik.se)
*
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
* or copy at http://opensource.org/licenses/MIT)
*/
#pragma once

#include <SQLiteCpp/Statement.h>




//this requires c++14. seems like visual studio 2015 should work (yet untested).
#if ( __cplusplus>= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) )
/// @cond
#include <utility>
#include <initializer_list>

namespace SQLite
{

/// implementation detail for variadic bind.
namespace detail {
template<class F,class ...Args, std::size_t ... I>
inline void invoke_with_index(F&& f, std::integer_sequence<std::size_t, I...>,
const Args& ...args) {
std::initializer_list<int> { (f(I+1,args),0)... };
}

/// implementation detail for variadic bind.
template<class F,class ...Args>
inline void invoke_with_index(F&&f, const Args& ... args) {
invoke_with_index(std::forward<F>(f),std::index_sequence_for<Args...>(), args...);
}

} //namespace detail
/// @endcond

/**
* \brief Convenience function for calling Statement::bind(...) once for each argument given.
*
* This takes care of incrementing the index between each calls to bind.
*
* This feature requires a c++14 capable compiler.
*
* \code{.cpp}
* SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC<?");
* bind(stm,a,b,c);
* //...is equivalent to
* stm.bind(1,a);
* stm.bind(2,b);
* stm.bind(3,c);
* \endcode
* @param s statement
* @param args one or more args to bind.
*/
template<class ...Args>
void bind(SQLite::Statement& s,const Args& ... args) {

static_assert(sizeof...(args)>0,"please invoke bind with one or more args");

auto f=[&s](std::size_t index, const auto& value) {
s.bind(index,value);
};
detail::invoke_with_index(f, args...);
}

} // namespace SQLite

#else
//not supported in older c++. provide a fallback?
#endif // c++14

77 changes: 77 additions & 0 deletions tests/VariadicBind_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* @file VariadicBind_test.cpp
* @ingroup tests
* @brief Test of variadic bind
*
* Copyright (c) 2016 Paul Dreik (github@pauldreik.se)
*
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
* or copy at http://opensource.org/licenses/MIT)
*/

#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Statement.h>
#include <SQLiteCpp/VariadicBind.h>

#include <gtest/gtest.h>

#include <cstdio>

//this requires c++14. seems like visual studio 2015 should work (yet untested).
#if ( __cplusplus>= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) )

TEST(VariadicBind, invalid) {
// Create a new database
#if 0
SQLite::Database db("variadic_bind_test.db3", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
#else
SQLite::Database db(":memory:", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
#endif

EXPECT_EQ(0, db.exec("DROP TABLE IF EXISTS test"));
EXPECT_EQ(0,
db.exec(
"CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT DEFAULT 'default') "));
EXPECT_TRUE(db.tableExists("test"));

{
SQLite::Statement query(db, "INSERT INTO test VALUES (?, ?)");

//zero arguments - should give compile time error through a static assert
//SQLite::bind(query);

//bind one argument less than expected - should be fine.
//the unspecified argument should be set to null, not the default.
SQLite::bind(query, 1);
EXPECT_EQ(1, query.exec());
query.reset();

//bind all arguments - should work just fine
SQLite::bind(query, 2, "two");
EXPECT_EQ(1, query.exec());
query.reset();

//bind too many arguments - should throw.
EXPECT_THROW(SQLite::bind(query, 3, "three", 0), SQLite::Exception);
EXPECT_EQ(1, query.exec());
}

//make sure the content is as expected
{
using namespace std::string_literals;

SQLite::Statement query(db, "SELECT id, value FROM test ORDER BY id"s);
std::vector<std::pair<int, std::string> > results;
while (query.executeStep()) {
const int id = query.getColumn(0);
std::string value = query.getColumn(1);
results.emplace_back( id, std::move(value) );
}
EXPECT_EQ(std::size_t(3), results.size());

EXPECT_EQ(std::make_pair(1,""s), results.at(0));
EXPECT_EQ(std::make_pair(2,"two"s), results.at(1));
EXPECT_EQ(std::make_pair(3,"three"s), results.at(2));
}
}
#endif // c++14