From b38e88decbaff0d446f9f204e3f4673daa3f07e6 Mon Sep 17 00:00:00 2001 From: maxbachmann <44199644+maxbachmann@users.noreply.github.com> Date: Sun, 16 Jun 2019 10:46:21 +0200 Subject: [PATCH] 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 --- .gitignore | 1 + CMakeLists.txt | 3 +- include/SQLiteCpp/ExecuteMany.h | 87 ++++++++++++++++++++++++++++++++ include/SQLiteCpp/VariadicBind.h | 59 +++++++++++++++++++--- tests/ExecuteMany_test.cpp | 55 ++++++++++++++++++++ tests/VariadicBind_test.cpp | 43 ++++++++++++++-- 6 files changed, 236 insertions(+), 12 deletions(-) create mode 100644 include/SQLiteCpp/ExecuteMany.h create mode 100644 tests/ExecuteMany_test.cpp diff --git a/.gitignore b/.gitignore index a1a2eaf..f22bfa2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ build example1 *.a +.vscode/ /SQLiteCpp.sln *.ncb *.suo diff --git a/CMakeLists.txt b/CMakeLists.txt index f3f5b7c..4fb82dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) - diff --git a/include/SQLiteCpp/ExecuteMany.h b/include/SQLiteCpp/ExecuteMany.h new file mode 100644 index 0000000..a6b90ad --- /dev/null +++ b/include/SQLiteCpp/ExecuteMany.h @@ -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 +#include + +/// @cond +#include +#include +#include + +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 +void execute_many(Database& aDatabase, const char* apQuery, Arg&& arg, Types&&... params) { + SQLite::Statement query(aDatabase, apQuery); + bind_exec(query, std::forward(arg)); + (void)std::initializer_list{ + ((void)reset_bind_exec(query, std::forward(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 +void reset_bind_exec(SQLite::Statement& query, std::tuple&& tuple) +{ + query.reset(); + bind_exec(query, std::forward(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 +void bind_exec(SQLite::Statement& query, std::tuple&& tuple) +{ + bind(query, std::forward(tuple)); + while (query.executeStep()) {} +} + +} // namespace SQLite + +#endif // c++14 diff --git a/include/SQLiteCpp/VariadicBind.h b/include/SQLiteCpp/VariadicBind.h index 2b9853c..f2a2aa5 100644 --- a/include/SQLiteCpp/VariadicBind.h +++ b/include/SQLiteCpp/VariadicBind.h @@ -16,6 +16,10 @@ #include +#if (__cplusplus >= 201402L) || ( defined(_MSC_VER) && (_MSC_VER >= 1900) ) // c++14: Visual Studio 2015 +#include +#endif // c++14 + /// @cond #include #include @@ -33,25 +37,66 @@ namespace SQLite * * \code{.cpp} * SQLite::Statement stm("SELECT * FROM MyTable WHERE colA>? && colB=? && colC -void bind(SQLite::Statement& s, const Args& ... args) +void bind(SQLite::Statement& query, const Args& ... args) { int pos = 0; (void)std::initializer_list{ - ((void)s.bind(++pos, std::forward(args)), 0)... + ((void)query.bind(++pos, std::forward(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 +void bind(SQLite::Statement& query, const std::tuple &tuple) +{ + bind(query, tuple, std::index_sequence_for()); +} + +/** + * \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 +void bind(SQLite::Statement& query, const std::tuple &tuple, std::index_sequence) +{ + bind(query, std::get(tuple)...); +} +#endif // c++14 + +} // namespace SQLite #endif // c++11 - diff --git a/tests/ExecuteMany_test.cpp b/tests/ExecuteMany_test.cpp new file mode 100644 index 0000000..8a4319f --- /dev/null +++ b/tests/ExecuteMany_test.cpp @@ -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 +#include +#include + +#include + +#include + +#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 > 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 diff --git a/tests/VariadicBind_test.cpp b/tests/VariadicBind_test.cpp index 8dcb2e7..20b0337 100644 --- a/tests/VariadicBind_test.cpp +++ b/tests/VariadicBind_test.cpp @@ -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 > 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