Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tuple bind and executeMany #197

Merged
merged 2 commits into from
Jun 16, 2019
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ build
example1
*.a

.vscode/
/SQLiteCpp.sln
*.ncb
*.suo
Expand Down
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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})

Expand All @@ -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})

Expand Down Expand Up @@ -325,4 +327,3 @@ if (SQLITECPP_BUILD_TESTS)
else (SQLITECPP_BUILD_TESTS)
message(STATUS "SQLITECPP_BUILD_TESTS OFF")
endif (SQLITECPP_BUILD_TESTS)

87 changes: 87 additions & 0 deletions include/SQLiteCpp/ExecuteMany.h
Original file line number Diff line number Diff line change
@@ -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
59 changes: 52 additions & 7 deletions include/SQLiteCpp/VariadicBind.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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

#endif // c++11
/**
* \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
55 changes: 55 additions & 0 deletions tests/ExecuteMany_test.cpp
Original file line number Diff line number Diff line change
@@ -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
43 changes: 39 additions & 4 deletions tests/VariadicBind_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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"});
Expand All @@ -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