add tuple_bind and execute_many (#197)

added the option to use a tuple with values to bind.

use this to implement executeMany as described in #24, so it will be possible to pass multiple tuples
This commit is contained in:
maxbachmann 2019-06-16 10:46:21 +02:00 committed by Sébastien Rombauts
parent 81913790f2
commit b38e88decb
6 changed files with 236 additions and 12 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@ build
example1
*.a
.vscode/
/SQLiteCpp.sln
*.ncb
*.suo

View File

@ -129,6 +129,7 @@ set(SQLITECPP_INC
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Transaction.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/Utils.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/VariadicBind.h
${PROJECT_SOURCE_DIR}/include/SQLiteCpp/ExecuteMany.h
)
source_group(include FILES ${SQLITECPP_INC})
@ -141,6 +142,7 @@ set(SQLITECPP_TESTS
tests/Transaction_test.cpp
tests/VariadicBind_test.cpp
tests/Exception_test.cpp
tests/ExecuteMany_test.cpp
)
source_group(tests FILES ${SQLITECPP_TESTS})
@ -325,4 +327,3 @@ if (SQLITECPP_BUILD_TESTS)
else (SQLITECPP_BUILD_TESTS)
message(STATUS "SQLITECPP_BUILD_TESTS OFF")
endif (SQLITECPP_BUILD_TESTS)

View File

@ -0,0 +1,87 @@
/**
* @file ExecuteMany.h
* @ingroup SQLiteCpp
* @brief Convenience function to execute a Statement with multiple Parameter sets
*
* Copyright (c) 2019 Maximilian Bachmann (github@maxbachmann)
*
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
* or copy at http://opensource.org/licenses/MIT)
*/
#pragma once
#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015
#include <SQLiteCpp/Statement.h>
#include <SQLiteCpp/VariadicBind.h>
/// @cond
#include <tuple>
#include <utility>
#include <initializer_list>
namespace SQLite
{
/// @endcond
/**
* \brief Convenience function to execute a Statement with multiple Parameter sets once for each parameter set given.
*
*
* This feature requires a c++14 capable compiler.
*
* \code{.cpp}
* execute_many(db, "INSERT INTO test VALUES (?, ?)",
* std::make_tuple(1, "one"),
* std::make_tuple(2, "two"),
* std::make_tuple(3, "three")
* );
* \endcode
* @param aDatabase Database to use
* @param apQuery Query to use with all parameter sets
* @param Arg first tuple with parameters
* @param Types the following tuples with parameters
*/
template <typename Arg, typename... Types>
void execute_many(Database& aDatabase, const char* apQuery, Arg&& arg, Types&&... params) {
SQLite::Statement query(aDatabase, apQuery);
bind_exec(query, std::forward<decltype(arg)>(arg));
(void)std::initializer_list<int>{
((void)reset_bind_exec(query, std::forward<decltype(params)>(params)), 0)...
};
}
/**
* \brief Convenience function to reset a statement and call bind_exec to
* bind new values to the statement and execute it
*
* This feature requires a c++14 capable compiler.
*
* @param apQuery Query to use
* @param tuple tuple to bind
*/
template <typename ... Types>
void reset_bind_exec(SQLite::Statement& query, std::tuple<Types...>&& tuple)
{
query.reset();
bind_exec(query, std::forward<decltype(tuple)>(tuple));
}
/**
* \brief Convenience function to bind values a the statement and execute it
*
* This feature requires a c++14 capable compiler.
*
* @param apQuery Query to use
* @param tuple tuple to bind
*/
template <typename ... Types>
void bind_exec(SQLite::Statement& query, std::tuple<Types...>&& tuple)
{
bind(query, std::forward<decltype(tuple)>(tuple));
while (query.executeStep()) {}
}
} // namespace SQLite
#endif // c++14

View File

@ -16,6 +16,10 @@
#include <SQLiteCpp/Statement.h>
#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015
#include <tuple>
#endif // c++14
/// @cond
#include <utility>
#include <initializer_list>
@ -33,25 +37,66 @@ namespace SQLite
*
* \code{.cpp}
* SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC<?");
* bind(stm,a,b,c);
* SQLite::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.
* @param query statement
* @param args zero or more args to bind.
*/
template<class ...Args>
void bind(SQLite::Statement& s, const Args& ... args)
void bind(SQLite::Statement& query, const Args& ... args)
{
int pos = 0;
(void)std::initializer_list<int>{
((void)s.bind(++pos, std::forward<decltype(args)>(args)), 0)...
((void)query.bind(++pos, std::forward<decltype(args)>(args)), 0)...
};
}
} // namespace SQLite
#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015
/**
* \brief Convenience function for calling Statement::bind(...) once for each parameter of a tuple,
* by forwarding them to the variadic template
*
* This feature requires a c++14 capable compiler.
*
* \code{.cpp}
* SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC<?");
* SQLite::bind(stm, std::make_tuple(a, b, c));
* //...is equivalent to
* stm.bind(1,a);
* stm.bind(2,b);
* stm.bind(3,c);
* \endcode
* @param query statement
* @param tuple tuple with values to bind
*/
template <typename ... Types>
void bind(SQLite::Statement& query, const std::tuple<Types...> &tuple)
{
bind(query, tuple, std::index_sequence_for<Types...>());
}
/**
* \brief Convenience function for calling Statement::bind(...) once for each parameter of a tuple,
* by forwarding them to the variadic template. This function is just needed to convert the tuples
* to parameter packs
*
* This feature requires a c++14 capable compiler.
*
* @param query statement
* @param tuple tuple with values to bind
*/
template <typename ... Types, std::size_t ... Indices>
void bind(SQLite::Statement& query, const std::tuple<Types...> &tuple, std::index_sequence<Indices...>)
{
bind(query, std::get<Indices>(tuple)...);
}
#endif // c++14
} // namespace SQLite
#endif // c++11

View File

@ -0,0 +1,55 @@
/**
* @file VariadicBind_test.cpp
* @ingroup tests
* @brief Test of variadic bind
*
* Copyright (c) 2016 Paul Dreik (github@pauldreik.se)
* Copyright (c) 2016-2019 Sebastien Rombauts (sebastien.rombauts@gmail.com)
* Copyright (c) 2019 Maximilian Bachmann (github@maxbachmann)
*
* 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/ExecuteMany.h>
#include <gtest/gtest.h>
#include <cstdio>
#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015
TEST(ExecuteMany, invalid) {
// Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
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"));
{
execute_many(db, "INSERT INTO test VALUES (?, ?)",
std::make_tuple(1),
std::make_tuple(2, "two"),
std::make_tuple(3, "three")
);
}
// make sure the content is as expected
{
SQLite::Statement query(db, std::string{"SELECT id, value FROM test ORDER BY id"});
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,std::string{""}), results.at(0));
EXPECT_EQ(std::make_pair(2,std::string{"two"}), results.at(1));
EXPECT_EQ(std::make_pair(3,std::string{"three"}), results.at(2));
}
}
#endif // c++14

View File

@ -28,14 +28,15 @@ TEST(VariadicBind, invalid) {
EXPECT_EQ(0,
db.exec(
"CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT DEFAULT 'default') "));
EXPECT_EQ(0,
db.exec(
"CREATE TABLE test2 (id INTEGER PRIMARY KEY, value TEXT DEFAULT 'default') "));
EXPECT_TRUE(db.tableExists("test"));
EXPECT_TRUE(db.tableExists("test2"));
{
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);
@ -51,7 +52,6 @@ TEST(VariadicBind, invalid) {
EXPECT_THROW(SQLite::bind(query, 3, "three", 0), SQLite::Exception);
EXPECT_EQ(1, query.exec());
}
// make sure the content is as expected
{
SQLite::Statement query(db, std::string{"SELECT id, value FROM test ORDER BY id"});
@ -67,5 +67,40 @@ TEST(VariadicBind, invalid) {
EXPECT_EQ(std::make_pair(2,std::string{"two"}), results.at(1));
EXPECT_EQ(std::make_pair(3,std::string{"three"}), results.at(2));
}
#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015
{
SQLite::Statement query(db, "INSERT INTO test2 VALUES (?, ?)");
// bind one argument less than expected - should be fine.
// the unspecified argument should be set to null, not the default.
SQLite::bind(query, std::make_tuple(1));
EXPECT_EQ(1, query.exec());
query.reset();
// bind all arguments - should work just fine
SQLite::bind(query, std::make_tuple(2, "two"));
EXPECT_EQ(1, query.exec());
query.reset();
// bind too many arguments - should throw.
EXPECT_THROW(SQLite::bind(query, std::make_tuple(3, "three", 0)), SQLite::Exception);
EXPECT_EQ(1, query.exec());
}
// make sure the content is as expected
{
SQLite::Statement query(db, std::string{"SELECT id, value FROM test2 ORDER BY id"});
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,std::string{""}), results.at(0));
EXPECT_EQ(std::make_pair(2,std::string{"two"}), results.at(1));
EXPECT_EQ(std::make_pair(3,std::string{"three"}), results.at(2));
}
#endif // c++14
}
#endif // c++11