diff options
author | John Turner <jturner.usa@gmail.com> | 2025-09-14 00:16:10 -0400 |
---|---|---|
committer | John Turner <jturner.usa@gmail.com> | 2025-09-14 00:16:10 -0400 |
commit | efcea3a80da7c4479d5fe168435ecc9fd06bdc72 (patch) | |
tree | 5cb0177e17b1b00a177f2e830e809f606334571b /test | |
download | sqlite-kv-bench-efcea3a80da7c4479d5fe168435ecc9fd06bdc72.tar.gz |
Squashed 'subprojects/boost-sqlite/' content from commit 3378e35
git-subtree-dir: subprojects/boost-sqlite
git-subtree-split: 3378e353705271e569cf4ba15c467b840a39798c
Diffstat (limited to 'test')
-rw-r--r-- | test/CMakeLists.txt | 11 | ||||
-rw-r--r-- | test/Jamfile | 25 | ||||
-rw-r--r-- | test/allocator.cpp | 23 | ||||
-rw-r--r-- | test/backup.cpp | 46 | ||||
-rw-r--r-- | test/blob.cpp | 56 | ||||
-rw-r--r-- | test/catch.cpp | 18 | ||||
-rw-r--r-- | test/collation.cpp | 45 | ||||
-rw-r--r-- | test/connection.cpp | 32 | ||||
-rw-r--r-- | test/extension/CMakeLists.txt | 19 | ||||
-rw-r--r-- | test/extension/simple_scalar.cpp | 27 | ||||
-rw-r--r-- | test/extension/simple_scalar.sql | 4 | ||||
-rw-r--r-- | test/field.cpp | 46 | ||||
-rw-r--r-- | test/function.cpp | 306 | ||||
-rw-r--r-- | test/hooks.cpp | 52 | ||||
-rw-r--r-- | test/json.cpp | 104 | ||||
-rw-r--r-- | test/main_test.cpp | 9 | ||||
-rw-r--r-- | test/meta_data.cpp | 45 | ||||
-rw-r--r-- | test/mutex.cpp | 31 | ||||
-rw-r--r-- | test/statement.cpp | 74 | ||||
-rw-r--r-- | test/static_resultset.cpp | 95 | ||||
-rw-r--r-- | test/test-db.sql | 29 | ||||
-rw-r--r-- | test/test.hpp | 22 | ||||
-rw-r--r-- | test/transaction.cpp | 89 | ||||
-rw-r--r-- | test/vtable.cpp | 256 |
24 files changed, 1464 insertions, 0 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..79750fb --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB ALL_TEST_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + +add_executable(boost_sqlite_tests ${ALL_TEST_FILES}) +target_link_libraries(boost_sqlite_tests PUBLIC SQLite::SQLite3 + Boost::json Boost::sqlite Boost::unit_test_framework) +target_compile_definitions(boost_sqlite_tests PUBLIC BOOST_SQLITE_SEPARATE_COMPILATION=1) + +add_test(NAME boost_sqlite_tests COMMAND boost_sqlite_tests) + +add_subdirectory(extension) + diff --git a/test/Jamfile b/test/Jamfile new file mode 100644 index 0000000..85d19ed --- /dev/null +++ b/test/Jamfile @@ -0,0 +1,25 @@ +# +# Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/boostorg/json +# + +project : +requirements <include>../../.. + +; + +import testing ; + +lib sqlite3 ; + +run [ glob *.cpp ] sqlite3 /boost//sqlite /boost//json /boost//unit_test_framework ; + + +lib simple_scalar : extension/simple_scalar.cpp /boost/sqlite//extension /boost//json + : <link>shared ; + +# TODO run simple_scalar as sqlite3 :memory: ".read extension/simple_scalar.sql"
\ No newline at end of file diff --git a/test/allocator.cpp b/test/allocator.cpp new file mode 100644 index 0000000..a9d9b7c --- /dev/null +++ b/test/allocator.cpp @@ -0,0 +1,23 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <boost/sqlite/allocator.hpp> +#include <boost/test/unit_test.hpp> + +using namespace boost; + +BOOST_AUTO_TEST_CASE(allocator) +{ + + sqlite::allocator<int> alloc; + + auto p = alloc.allocate(32); + BOOST_CHECK(p != nullptr); + alloc.deallocate(p, 32); + + BOOST_CHECK_THROW(boost::ignore_unused(alloc.allocate((std::numeric_limits<std::size_t>::max)())), std::bad_alloc); +} diff --git a/test/backup.cpp b/test/backup.cpp new file mode 100644 index 0000000..5a42c50 --- /dev/null +++ b/test/backup.cpp @@ -0,0 +1,46 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + + +#include <boost/sqlite/backup.hpp> +#include <boost/sqlite/connection.hpp> + +#include <string> +#include <vector> + +#include "test.hpp" + +using namespace boost; + +BOOST_AUTO_TEST_CASE(backup) +{ + sqlite::connection conn1{":memory:"}; + conn1.execute( +#include "test-db.sql" + ); + // language=sqlite + conn1.query("select * from author;"); + + + sqlite::connection conn2{":memory:"}; + sqlite::backup(conn1, conn2); + + std::vector<std::string> names1, names2; + + // language=sqlite + for (auto r : conn1.query("select first_name from author;")) + names1.emplace_back(r.at(0u).get_text()); + + // language=sqlite + for (auto r : conn2.query("select first_name from author;")) + names2.emplace_back(r.at(0u).get_text()); + + BOOST_CHECK(!names1.empty()); + BOOST_CHECK(!names2.front().empty()); + BOOST_CHECK(names1 == names2); + BOOST_CHECK_THROW(sqlite::backup(conn1, conn2, "foo", "bar"), boost::system::system_error); +}
\ No newline at end of file diff --git a/test/blob.cpp b/test/blob.cpp new file mode 100644 index 0000000..ca6ce83 --- /dev/null +++ b/test/blob.cpp @@ -0,0 +1,56 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + + +#include <boost/sqlite/blob.hpp> +#include <boost/sqlite/connection.hpp> + +#include <random> + +#include "test.hpp" + +using namespace boost; + +BOOST_AUTO_TEST_CASE(blob) +{ + sqlite::connection conn{":memory:"}; + // language=sqlite + conn.execute("create table blobs(id integer primary key autoincrement, bb blob);"); + + std::vector<unsigned char> blobby; + blobby.resize(4096*4096); + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_int_distribution<std::mt19937::result_type> dist(0,255); // distribution in range [1, 6] + + std::generate(blobby.begin(), blobby.end(), + [&]{return static_cast<unsigned char>(dist(rng));}); + + + conn.prepare("insert into blobs(bb) values ($1);").execute(std::make_tuple(sqlite::zero_blob(4096 * 4096 ))); + + auto bh = open_blob(conn, "main", "blobs", "bb", 1); + + BOOST_CHECK(bh.size() == 4096 * 4096); + + + unsigned char buf[4096]; + std::generate(std::begin(buf), std::end(buf), [&]{return static_cast<unsigned char>(dist(rng));}); + bh.read_at(buf, 4096, 4096); + BOOST_CHECK(std::all_of(std::begin(buf), std::end(buf), [](unsigned char c) {return c == 0u;})); + + bh.write_at(blobby.data(), blobby.size(), 0u); + bh.read_at(buf, 4096, 4096); + BOOST_CHECK(std::memcmp(buf, blobby.data() + 4096, 4096) == 0); + + BOOST_CHECK_THROW(open_blob(conn, "main", "doesnt-exit", "blobber", 2), boost::system::system_error); + + sqlite::blob_handle bb; + BOOST_CHECK_THROW(bb.read_at(blobby.data(), blobby.size(), 0), boost::system::system_error); + BOOST_CHECK_THROW(bb.write_at(blobby.data(), blobby.size(), 0), boost::system::system_error); + +}
\ No newline at end of file diff --git a/test/catch.cpp b/test/catch.cpp new file mode 100644 index 0000000..37b8dbb --- /dev/null +++ b/test/catch.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2023 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/sqlite/detail/catch.hpp> +#include <boost/test/unit_test.hpp> +using namespace boost; + +BOOST_AUTO_TEST_CASE(prefix) +{ + system::system_error se(SQLITE_TOOBIG, sqlite::sqlite_category()); + BOOST_CHECK(sqlite::detail::get_message(se).empty()); + + se = system::system_error(SQLITE_TOOBIG, sqlite::sqlite_category(), "foobar"); + + BOOST_CHECK(sqlite::detail::get_message(se) == "foobar"); +}
\ No newline at end of file diff --git a/test/collation.cpp b/test/collation.cpp new file mode 100644 index 0000000..8893e42 --- /dev/null +++ b/test/collation.cpp @@ -0,0 +1,45 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <boost/sqlite/collation.hpp> +#include <string> +#include <vector> + +#include "test.hpp" + +using namespace boost; + +struct collate_length +{ + int operator()(core::string_view l, core::string_view r) noexcept + { + return std::stoull(r) - l.size(); + } +}; + +BOOST_AUTO_TEST_CASE(collation) +{ + sqlite::connection conn(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + sqlite::create_collation(conn, "length", collate_length{}); + + std::vector<std::string> names; + + // language=sqlite + for (auto r : conn.query("select first_name from author where first_name = 5 collate length order by last_name asc;")) + names.emplace_back(r.at(0).get_text()); + + std::vector<std::string> cmp = {"peter", "ruben"}; + BOOST_CHECK(names == cmp); + + sqlite::delete_collation(conn, "length"); + + BOOST_CHECK_THROW(conn.query("select first_name from author where first_name = 5 collate length order by last_name asc;"), system::system_error); +}
\ No newline at end of file diff --git a/test/connection.cpp b/test/connection.cpp new file mode 100644 index 0000000..b3c63ca --- /dev/null +++ b/test/connection.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/sqlite/connection.hpp> +#include "test.hpp" + +#include <filesystem> + +using namespace boost; + + +BOOST_AUTO_TEST_CASE(connection) +{ + sqlite::connection conn; + conn.connect(std::filesystem::path(":memory:")); + conn.execute( +#include "test-db.sql" + ); + + BOOST_CHECK_THROW(conn.execute("elect * from nothing;"), boost::system::system_error); + conn.close(); +} + +BOOST_AUTO_TEST_CASE(exc) +{ + sqlite::connection conn; + conn.connect(sqlite::in_memory); + BOOST_CHECK_THROW(conn.execute("select 932 fro 12;"), boost::system::system_error); + conn.close(); +}
\ No newline at end of file diff --git a/test/extension/CMakeLists.txt b/test/extension/CMakeLists.txt new file mode 100644 index 0000000..a52a92c --- /dev/null +++ b/test/extension/CMakeLists.txt @@ -0,0 +1,19 @@ + +file(GLOB_RECURSE ALL_SQLITE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.sql) +file(GLOB_RECURSE ALL_CPP_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + +foreach(module ${ALL_CPP_FILES}) + get_filename_component(stem ${module} NAME_WE) + add_library(boost_sqlite_test_extension_${stem} SHARED ${module}) + target_link_libraries(boost_sqlite_test_extension_${stem} PUBLIC Boost::sqlite_ext) + target_include_directories(boost_sqlite_test_extension_${stem} PUBLIC ../../include) + set_property(TARGET boost_sqlite_test_extension_${stem} PROPERTY PREFIX "") + set_target_properties(boost_sqlite_test_extension_${stem} PROPERTIES OUTPUT_NAME ${stem}) +endforeach() + +foreach(script ${ALL_SQLITE_FILES}) + get_filename_component(stem ${script} NAME_WE) + add_test(NAME boost_sqlite_test_extension_${stem} COMMAND + sqlite3 :memory: ".read ${CMAKE_CURRENT_SOURCE_DIR}/${script}" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +endforeach() diff --git a/test/extension/simple_scalar.cpp b/test/extension/simple_scalar.cpp new file mode 100644 index 0000000..4a05afe --- /dev/null +++ b/test/extension/simple_scalar.cpp @@ -0,0 +1,27 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <boost/sqlite/extension.hpp> +#include <boost/sqlite/function.hpp> + +BOOST_SQLITE_EXTENSION(simplescalar, conn) +{ + create_scalar_function( + conn, "assert", + [](boost::sqlite::context<>, boost::span<boost::sqlite::value, 1u> sp) + { + if (sp.front().get_int() == 0) + throw std::logic_error("test failed"); + }); + + create_scalar_function( + conn, "my_add", + [](boost::sqlite::context<>, boost::span<boost::sqlite::value, 2u> sp)-> sqlite3_int64 + { + return sp[0].get_int() + sp[1].get_int(); + }); +} diff --git a/test/extension/simple_scalar.sql b/test/extension/simple_scalar.sql new file mode 100644 index 0000000..00064ef --- /dev/null +++ b/test/extension/simple_scalar.sql @@ -0,0 +1,4 @@ +SELECT load_extension('./simple_scalar'); + +select assert(5 = (select my_add(2, 3))); +select assert(7 = (select my_add(4, 3))); diff --git a/test/field.cpp b/test/field.cpp new file mode 100644 index 0000000..a3fae32 --- /dev/null +++ b/test/field.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/sqlite/field.hpp> +#include <boost/sqlite/connection.hpp> +#include "test.hpp" + +using namespace boost; + +BOOST_AUTO_TEST_CASE(field) +{ + sqlite::connection conn(":memory:"); + // language=sqlite + conn.execute(R"( +create table type_tester( + id integer primary key autoincrement, + num real, + nl null, + txt text, + blb blob); + + insert into type_tester values(42, 1.2, null, 'text', x'04050607'); +)"); + + auto res = conn.query("select * from type_tester"); + auto r = res.current(); + + BOOST_CHECK(r[0].type() == sqlite::value_type::integer); + BOOST_CHECK(r[0].get_int() == 42); + + BOOST_CHECK(r[1].type() == sqlite::value_type::floating); + BOOST_CHECK(r[1].get_double() == 1.2); + + BOOST_CHECK(r[2].type() == sqlite::value_type::null); + BOOST_CHECK(r[3].type() == sqlite::value_type::text); + BOOST_CHECK(r[3].get_text() == "text"); + + BOOST_CHECK(r[4].type() == sqlite::value_type::blob); + + sqlite::blob bl{4u}; + char raw_data[4] = {4,5,6,7}; + std::memcpy(bl.data(), raw_data, 4); + BOOST_CHECK(std::memcmp(bl.data(), r[4].get_blob().data(), 4u) == 0u); +}
\ No newline at end of file diff --git a/test/function.cpp b/test/function.cpp new file mode 100644 index 0000000..b9a4c57 --- /dev/null +++ b/test/function.cpp @@ -0,0 +1,306 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/sqlite/function.hpp> +#include <boost/sqlite/connection.hpp> +#include "test.hpp" + +#include <string> +#include <vector> + +using namespace boost; + +BOOST_AUTO_TEST_CASE(scalar) +{ + sqlite::connection conn(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + sqlite::create_scalar_function( + conn, + "to_upper", + [](sqlite::context<>, boost::span<sqlite::value, 1u> val) + -> variant2::variant<variant2::monostate, sqlite::value, std::string> + { + if (val.empty()) + return {}; + + if (val[0].type() != sqlite::value_type::text) + return val[0]; + + auto txt = val[0].get_text(); + std::string res; + res.resize(txt.size()); + std::transform(txt.begin(), txt.end(), res.begin(), [](char c){return std::toupper(c);}); + return res; + }); + + std::vector<std::string> names; + + // language=sqlite + for (auto r : conn.query("select to_upper(first_name) from author order by last_name asc;")) + names.emplace_back(r.at(0).get_text()); + + + std::vector<std::string> nm = {"PETER", "VINNIE", "RICHARD", "RUBEN"}; + BOOST_CHECK(nm == names); +} + + +BOOST_AUTO_TEST_CASE(scalar_pointer) +{ + sqlite::connection conn(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + sqlite::create_scalar_function( + conn, + "to_upper", + +[](sqlite::context<>, boost::span<sqlite::value, 1u> val) + -> variant2::variant<variant2::monostate, sqlite::value, std::string> + { + if (val.empty()) + return {}; + + if (val[0].type() != sqlite::value_type::text) + return val[0]; + + auto txt = val[0].get_text(); + std::string res; + res.resize(txt.size()); + std::transform(txt.begin(), txt.end(), res.begin(), [](char c){return std::toupper(c);}); + return res; + }); + + std::vector<std::string> names; + + // language=sqlite + for (auto r : conn.query("select to_upper(first_name) from author order by last_name asc;")) + names.emplace_back(r.at(0).get_text()); + + + std::vector<std::string> nm = {"PETER", "VINNIE", "RICHARD", "RUBEN"}; + BOOST_CHECK(nm == names); +} + + +BOOST_AUTO_TEST_CASE(scalar_void) +{ + sqlite::connection conn(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + sqlite::create_scalar_function( + conn, + "to_upper", + [](sqlite::context<>, boost::span<sqlite::value, 1u> ) + { + return ; + }); + + + // language=sqlite + for (auto r : conn.query("select to_upper(first_name) from author order by last_name asc;")) + BOOST_CHECK(r[0].is_null()); +} + + +BOOST_AUTO_TEST_CASE(scalar_void_pointer) +{ + sqlite::connection conn(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + sqlite::create_scalar_function( + conn, + "to_upper", + +[](sqlite::context<>, boost::span<sqlite::value, 1u> ) + { + return ; + }); + + std::vector<std::string> names; + + // language=sqlite + for (auto r : conn.query("select to_upper(first_name) from author order by last_name asc;")) + BOOST_CHECK(r[0].is_null()); +} + + + +BOOST_AUTO_TEST_CASE(aggregate) +{ + sqlite::connection conn(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + struct aggregate_func + { + aggregate_func(int value) : counter(value) {} + std::size_t counter; + void step(boost::span<sqlite::value, 1u> val) + { + counter += val[0].get_text().size(); + } + + std::int64_t final() + { + return counter; + } + }; + + sqlite::create_aggregate_function<aggregate_func>( + conn, + "char_counter", std::make_tuple(0)); + + std::vector<std::size_t> lens; + + // language=sqlite + for (auto r : conn.query("select char_counter(first_name) from author;")) + lens.emplace_back(r.at(0).get_int()); + + BOOST_CHECK(lens.size() == 1u); + BOOST_CHECK(lens[0] == (5 + 6 + 7 + 5)); +} + +BOOST_AUTO_TEST_CASE(aggregate_result) +{ + sqlite::connection conn(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + struct aggregate_func + { + aggregate_func(int value) : counter(value) {} + std::size_t counter; + sqlite::result<void> step(boost::span<sqlite::value, 1u> val) + { + counter += val[0].get_text().size(); + return {}; + } + + sqlite::result<std::int64_t> final() + { + return counter; + } + }; + + sqlite::create_aggregate_function<aggregate_func>( + conn, + "char_counter", std::make_tuple(0)); + + std::vector<std::size_t> lens; + + // language=sqlite + for (auto r : conn.query("select char_counter(first_name) from author;")) + lens.emplace_back(r.at(0).get_int()); + + BOOST_CHECK(lens.size() == 1u); + BOOST_CHECK(lens[0] == (5 + 6 + 7 + 5)); +} + +#if SQLITE_VERSION_NUMBER >= 3025000 +BOOST_AUTO_TEST_CASE(window) +{ + sqlite::connection conn(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + struct window_func + { + std::size_t counter; + void step(boost::span<sqlite::value, 1u> val) + { + counter += val[0].get_text().size(); + } + + void inverse(boost::span<sqlite::value, 1u> val) + { + counter -= val[0].get_text().size(); + } + + + std::int64_t value() + { + return counter; + } + }; + + sqlite::create_window_function<window_func>( + conn, + "win_counter"); + + std::vector<std::size_t> lens; + + // language=sqlite + for (auto r : conn.query(R"( +select win_counter(first_name) over ( + order by last_name rows between 1 preceding and 1 following ) as subrows + from author order by last_name asc;)")) + lens.emplace_back(r.at(0).get_int()); + + BOOST_CHECK(lens.size() == 4u); + BOOST_CHECK(lens[0] == 11); + BOOST_CHECK(lens[1] == 18); + BOOST_CHECK(lens[2] == 18); + BOOST_CHECK(lens[3] == 12); +} + +BOOST_AUTO_TEST_CASE(window_result) +{ + sqlite::connection conn(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + struct window_func + { + std::size_t counter; + sqlite::result<void> step(boost::span<sqlite::value, 1u> val) + { + counter += val[0].get_text().size(); + return{}; + } + + sqlite::result<void> inverse(boost::span<sqlite::value, 1u> val) + { + counter -= val[0].get_text().size(); + return {}; + } + + + sqlite::result<std::int64_t> value() + { + return counter; + } + }; + + sqlite::create_window_function<window_func>( + conn, + "win_counter"); + + std::vector<std::size_t> lens; + + // language=sqlite + for (auto r : conn.query(R"( +select win_counter(first_name) over ( + order by last_name rows between 1 preceding and 1 following ) as subrows + from author order by last_name asc;)")) + lens.emplace_back(r.at(0).get_int()); + + BOOST_CHECK(lens.size() == 4u); + BOOST_CHECK(lens[0] == 11); + BOOST_CHECK(lens[1] == 18); + BOOST_CHECK(lens[2] == 18); + BOOST_CHECK(lens[3] == 12); +} +#endif diff --git a/test/hooks.cpp b/test/hooks.cpp new file mode 100644 index 0000000..2aa371f --- /dev/null +++ b/test/hooks.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/sqlite/hooks.hpp> +#include <boost/sqlite/connection.hpp> +#include "test.hpp" + +using namespace boost; + +BOOST_AUTO_TEST_CASE(hooks) +{ + sqlite::connection conn(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + bool called = false; + auto l = + [&](int op, core::string_view db, core::string_view table, sqlite3_int64 ) noexcept + { + BOOST_CHECK(op == SQLITE_INSERT); + BOOST_CHECK(db == "main"); + BOOST_CHECK(table == "library"); + called = true; + }; + + + sqlite::update_hook(conn, l); + // language=sqlite + conn.query(R"( + insert into library ("name", "author") values + ('mustache',(select id from author where first_name = 'peter' and last_name = 'dimov')); + )"); + + BOOST_CHECK(called); + +#if defined(SQLITE_ENABLE_PREUPDATE_HOOK) + auto hk = [](sqlite::preupdate_context ctx, + int op, + const char * db_name, + const char * table_name, + sqlite3_int64 current_key, + sqlite3_int64 new_key) noexcept + { + + }; + + preupdate_hook(conn, hk); +#endif +} diff --git a/test/json.cpp b/test/json.cpp new file mode 100644 index 0000000..fe924ab --- /dev/null +++ b/test/json.cpp @@ -0,0 +1,104 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/sqlite/json.hpp> +#include <boost/sqlite/connection.hpp> +#include <boost/sqlite/function.hpp> +#include <boost/json.hpp> +#include "test.hpp" + +using namespace boost; + + +BOOST_AUTO_TEST_SUITE(json_); + +BOOST_AUTO_TEST_CASE(to_value) +{ + sqlite::connection conn(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + auto q = conn.prepare("select 'foo', json_array($1, '2', null)").execute(std::make_tuple(1)); + + sqlite::row r = q.current(); + + BOOST_CHECK(!sqlite::is_json(r[0])); + BOOST_CHECK( sqlite::is_json(r[1])); + BOOST_CHECK(sqlite::as_json(r[1]) == (json::array{1, "2", nullptr})); + BOOST_CHECK(json::value_from(r[1]) == (json::array{1, "2", nullptr})); + BOOST_CHECK(!q.read_next()); + BOOST_CHECK(!q.read_next()); + + // language=sqlite + q = conn.query(R"(select first_name, "name" from library inner join author a on a.id = library.author order by library.name asc)"); + + auto js = json::value_from(std::move(q)); + + json::array aa { + { + {"first_name", "vinnie"}, + {"name", "beast"} + }, + { + {"first_name", "peter"}, + {"name", "mp11"} + }, + { + {"first_name", "ruben"}, + {"name", "mysql"} + }, + { + {"first_name","peter"}, + {"name", "variant2"} + }, + }; + + BOOST_CHECK(aa == aa); +}; + +BOOST_AUTO_TEST_CASE(blob) +{ + sqlite::connection conn(":memory:"); + BOOST_CHECK_THROW(json::value_from(conn.prepare("select $1;").execute({sqlite::zero_blob(1024)})), std::invalid_argument); + BOOST_CHECK(nullptr == json::value_from(conn.query("select null;").current().at(0))); + BOOST_CHECK(1234 == json::value_from(conn.query("select 1234;"). current().at(0))); + BOOST_CHECK(12.4 == json::value_from(conn.query("select 12.4;"). current().at(0))); +} + +BOOST_AUTO_TEST_CASE(value) +{ + sqlite::connection conn(":memory:"); + BOOST_CHECK_THROW(json::value_from(conn.prepare("select $1;").execute({sqlite::zero_blob(1024)}).current().at(0)), std::invalid_argument); + BOOST_CHECK(nullptr == json::value_from(conn.query("select null;").current().at(0).get_value())); + BOOST_CHECK(1234 == json::value_from(conn.query("select 1234;"). current().at(0).get_value())); + BOOST_CHECK(12.4 == json::value_from(conn.query("select 12.4;"). current().at(0).get_value())); + BOOST_CHECK("txt" == json::value_from(conn.query("select 'txt';"). current().at(0).get_value())); +} + + +BOOST_AUTO_TEST_CASE(subtype) +{ + sqlite::connection conn(":memory:"); + BOOST_CHECK(!sqlite::is_json(conn.prepare("select $1;").execute({"foobar"}).current().at(0))); + BOOST_CHECK(sqlite::is_json(conn.prepare("select json_array($1);").execute({"foobar"}).current().at(0))); +} + + +BOOST_AUTO_TEST_CASE(function) +{ + sqlite::connection conn(":memory:"); + sqlite::create_scalar_function(conn, "my_json_parse", + [](boost::sqlite::context<> , boost::span<boost::sqlite::value, 1u> s) + { + return json::parse(s[0].get_text()); + }); + + BOOST_CHECK(sqlite::is_json(conn.prepare("select my_json_parse($1);") + .execute({R"({"foo" : 42, "bar" : "xyz"})"}).current().at(0))); +} + + +BOOST_AUTO_TEST_SUITE_END();
\ No newline at end of file diff --git a/test/main_test.cpp b/test/main_test.cpp new file mode 100644 index 0000000..23c2f70 --- /dev/null +++ b/test/main_test.cpp @@ -0,0 +1,9 @@ +// Copyright (c) 2021 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TEST_MODULE sqlite_test +#include <boost/test/unit_test.hpp> + +#include <boost/sqlite.hpp> diff --git a/test/meta_data.cpp b/test/meta_data.cpp new file mode 100644 index 0000000..2861a1c --- /dev/null +++ b/test/meta_data.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/sqlite/meta_data.hpp> +#include <boost/sqlite/connection.hpp> +#include "test.hpp" +#include <boost/algorithm/string.hpp> + +using namespace boost; + +BOOST_AUTO_TEST_CASE(meta_data) +{ + sqlite::connection conn; + conn.connect(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + + + auto fn = table_column_meta_data(conn, "author", "first_name"); + BOOST_CHECK_MESSAGE(boost::iequals(fn.data_type, "TEXT"), fn.data_type); + BOOST_CHECK(!fn.auto_increment); + BOOST_CHECK_MESSAGE(boost::iequals(fn.collation, "BINARY"), fn.collation); + BOOST_CHECK(!fn.primary_key); + BOOST_CHECK( fn.not_null); + + auto ln = table_column_meta_data(conn, "main", "author", "last_name"); + BOOST_CHECK_MESSAGE(boost::iequals(ln.data_type, "TEXT"), ln.data_type); + BOOST_CHECK(!ln.auto_increment); + BOOST_CHECK_MESSAGE(boost::iequals(ln.collation, "BINARY"), ln.collation); + BOOST_CHECK(!ln.primary_key); + BOOST_CHECK(!ln.not_null); + + auto id = table_column_meta_data(conn, "main", "author", "id"); + BOOST_CHECK(boost::iequals(id.data_type, "INTEGER")); + BOOST_CHECK( id.auto_increment); + BOOST_CHECK(boost::iequals(id.collation, "BINARY")); + BOOST_CHECK( id.primary_key); + BOOST_CHECK( id.not_null); + + conn.close(); +}
\ No newline at end of file diff --git a/test/mutex.cpp b/test/mutex.cpp new file mode 100644 index 0000000..89023e1 --- /dev/null +++ b/test/mutex.cpp @@ -0,0 +1,31 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + + +#include <boost/sqlite/mutex.hpp> + +#include <boost/test/unit_test.hpp> +#include <mutex> + +using namespace boost; + +BOOST_AUTO_TEST_CASE(mutex) +{ + sqlite::mutex mtx; + sqlite::recursive_mutex rmtx; + BOOST_CHECK(mtx.try_lock()); + BOOST_CHECK(!mtx.try_lock()); + mtx.unlock(); + + std::lock_guard<sqlite::mutex> l1{mtx}; + std::lock_guard<sqlite::recursive_mutex> l2{rmtx}; + std::lock_guard<sqlite::recursive_mutex> l{rmtx}; + + BOOST_CHECK(rmtx.try_lock()); + BOOST_CHECK(rmtx.try_lock()); + BOOST_CHECK(rmtx.try_lock()); +}
\ No newline at end of file diff --git a/test/statement.cpp b/test/statement.cpp new file mode 100644 index 0000000..b9e96f6 --- /dev/null +++ b/test/statement.cpp @@ -0,0 +1,74 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <boost/sqlite/connection.hpp> +#include "test.hpp" + +#include <boost/json.hpp> +#include <boost/algorithm/string.hpp> + +#include <unordered_map> + + +using namespace boost; + + +BOOST_AUTO_TEST_CASE(statement) +{ + sqlite::connection conn; + conn.connect(":memory:"); +#if SQLITE_VERSION_NUMBER >= 3020000 + std::unique_ptr<int> data{new int(42)}; + + auto ip = data.get(); + auto q = conn.prepare("select $1;").execute(std::make_tuple(std::move(data))); + sqlite::row r = q.current(); + BOOST_CHECK(r.size() == 1u); + + auto v = r.at(0).get_value(); + BOOST_CHECK(v.type() == sqlite::value_type::null); + BOOST_CHECK(v.get_pointer<int>() != nullptr); + BOOST_CHECK(v.get_pointer<int>() == ip); + BOOST_CHECK(v.get_pointer<double>() == nullptr); +#endif + BOOST_CHECK_THROW(conn.prepare("select * from nothing where name = $name;").execute({}), boost::system::system_error); +} + + +BOOST_AUTO_TEST_CASE(decltype_) +{ + sqlite::connection conn; + conn.connect(":memory:"); + conn.execute( +#include "test-db.sql" + ); + auto q = conn.prepare("select* from author;"); + + BOOST_CHECK(boost::iequals(q.declared_type(0), "INTEGER")); + BOOST_CHECK(boost::iequals(q.declared_type(1), "TEXT")); + BOOST_CHECK(boost::iequals(q.declared_type(2), "TEXT")); + + BOOST_CHECK_THROW(conn.prepare("elect * from nothing;"), boost::system::system_error); +} + + +BOOST_AUTO_TEST_CASE(map) +{ + sqlite::connection conn; + conn.connect(":memory:"); + conn.execute( +#include "test-db.sql" + ); + auto q = conn.prepare("select * from author where first_name = $name;").execute({{"name", 42}}); + BOOST_CHECK_THROW(conn.prepare("select * from nothing where name = $name;").execute({{"n4ame", 123}}), boost::system::system_error); + + std::unordered_map<std::string, variant2::variant<int, std::string>> params = {{"name", 42}}; + q = conn.prepare("select * from author where first_name = $name;").execute(params); + BOOST_CHECK_THROW(conn.prepare("select * from nothing where name = $name;").execute(params), boost::system::system_error); + + BOOST_CHECK_THROW(conn.prepare("elect * from nothing;"), boost::system::system_error); +}
\ No newline at end of file diff --git a/test/static_resultset.cpp b/test/static_resultset.cpp new file mode 100644 index 0000000..6a9b7c0 --- /dev/null +++ b/test/static_resultset.cpp @@ -0,0 +1,95 @@ +// +// Copyright (c) 2024 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + + +#include <boost/sqlite/static_resultset.hpp> +#include <boost/sqlite/connection.hpp> + +#include <boost/describe/class.hpp> + +#include "test.hpp" + +using namespace boost; + +BOOST_AUTO_TEST_CASE(tuple) +{ + sqlite::connection conn; + conn.connect(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + using tup = std::tuple<sqlite_int64, sqlite::string_view, sqlite::string_view>; + + bool found = false; + for (tup t : conn.query<tup>("select id, first_name, last_name from author where last_name = 'hodges';")) + { + BOOST_CHECK(std::get<1>(t) == "richard"); + found = true; + } + BOOST_CHECK(found); + + found = false; + for (tup t : conn.prepare("select id, first_name, last_name from author where last_name = ?;") + .execute<tup>({"hodges"})) + { + BOOST_CHECK(std::get<1>(t) == "richard"); + found = true; + } + BOOST_CHECK(found); + + BOOST_CHECK_THROW(conn.query<tup>("select first_name, last_name from author where last_name = 'hodges';"), + system::system_error); + conn.close(); +} + +struct author +{ + std::string last_name; + std::string first_name; +}; + +#if __cplusplus < 202002L +BOOST_DESCRIBE_STRUCT(author, (), (last_name, first_name)); +#endif + +#if __cplusplus > 201402L + +BOOST_AUTO_TEST_CASE(reflection) +{ + sqlite::connection conn; + conn.connect(":memory:"); + conn.execute( +#include "test-db.sql" + ); + + + bool found = false; + for (author t : conn.query<author>("select first_name, last_name from author where last_name = 'hodges';")) + { + BOOST_CHECK(t.first_name == "richard"); + BOOST_CHECK(t.last_name == "hodges"); + found = true; + } + BOOST_CHECK(found); + + found = false; + for (author t : conn.prepare("select first_name, last_name from author where last_name = ?;") + .execute<author>({"hodges"})) + { + BOOST_CHECK(t.first_name == "richard"); + BOOST_CHECK(t.last_name == "hodges"); + found = true; + } + BOOST_CHECK(found); + + BOOST_CHECK_THROW(conn.query<author>("select id, first_name, last_name from author where last_name = 'hodges';"), + system::system_error); + conn.close(); +} + +#endif diff --git a/test/test-db.sql b/test/test-db.sql new file mode 100644 index 0000000..251313c --- /dev/null +++ b/test/test-db.sql @@ -0,0 +1,29 @@ +--query_helper(R"( + +create table author ( + id integer primary key autoincrement not null, + first_name text unique not null, + last_name text +); + +insert into author (first_name, last_name) values + ('vinnie', 'falco'), + ('richard', 'hodges'), + ('ruben', 'perez'), + ('peter', 'dimov') +; + +create table library( + id integer primary key autoincrement, + name text unique, + author integer references author(id) +); + +insert into library ("name", author) values + ('beast', (select id from author where first_name = 'vinnie' and last_name = 'falco')), + ('mysql', (select id from author where first_name = 'ruben' and last_name = 'perez')), + ('mp11', (select id from author where first_name = 'peter' and last_name = 'dimov')), + ('variant2', (select id from author where first_name = 'peter' and last_name = 'dimov')) +; + +--)")
\ No newline at end of file diff --git a/test/test.hpp b/test/test.hpp new file mode 100644 index 0000000..3244d9e --- /dev/null +++ b/test/test.hpp @@ -0,0 +1,22 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_SQLITE_TEST_HPP +#define BOOST_SQLITE_TEST_HPP + +#include <boost/test/unit_test.hpp> + +struct query_helper +{ + const char * query; + query_helper(const char * query) : query(query) {} + + const char * operator--() const {return query;} +}; + + +#endif //BOOST_SQLITE_TEST_HPP diff --git a/test/transaction.cpp b/test/transaction.cpp new file mode 100644 index 0000000..f1331b7 --- /dev/null +++ b/test/transaction.cpp @@ -0,0 +1,89 @@ +// +// Copyright (c) 2024 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <boost/sqlite/transaction.hpp> + +#include "test.hpp" + +using namespace boost; + +BOOST_AUTO_TEST_CASE(transaction) +{ + sqlite::connection conn{":memory:"}; + conn.execute("create table test(nr integer);"); + + auto check_size = [&]{ + std::size_t n = 0ull; + for (auto l : conn.query("select * from test")) + { + boost::ignore_unused(l); + n++; + } + + return n; + }; + + { + sqlite::transaction t{conn}; + BOOST_CHECK_THROW(sqlite::transaction{conn}, system::system_error); + + conn.execute("insert into test values(1), (2)"); + BOOST_CHECK(check_size() == 2); + + { + sqlite::savepoint sq{conn, "s1"}; + conn.execute("insert into test values(3)"); + BOOST_CHECK(check_size() == 3); + } + + BOOST_CHECK(check_size() == 2); + + { + sqlite::savepoint sq{conn, "s1"}; + conn.execute("insert into test values(4)"); + BOOST_CHECK(check_size() == 3); + sq.commit(); + } + + BOOST_CHECK(check_size() == 3); + + { + sqlite::savepoint sq{conn, "s1"}; + conn.execute("insert into test values(5)"); + BOOST_CHECK(check_size() == 4); + { + sqlite::savepoint sq2{conn, "s2"}; + conn.execute("insert into test values (6), (7)"); + BOOST_CHECK(check_size() == 6); + sq2.commit(); + } + BOOST_CHECK_EQUAL(check_size(), 6u); + } + BOOST_CHECK_EQUAL(check_size(), 3u); + + } + + BOOST_CHECK(conn.query("select * from test").done()); + + { + system::error_code ec; + sqlite::error_info ei; + conn.execute("BEGIN", ec, ei); + BOOST_CHECK(!ec); + conn.execute("BEGIN", ec, ei); + BOOST_CHECK(ec); + + BOOST_CHECK_THROW(sqlite::transaction{conn}, system::system_error); + sqlite::transaction t{conn, sqlite::transaction::adopt_transaction}; + + conn.execute("insert into test values (42), (3);"); + t.commit(); + } + + BOOST_CHECK_EQUAL(check_size(), 2u); +} + diff --git a/test/vtable.cpp b/test/vtable.cpp new file mode 100644 index 0000000..4f38732 --- /dev/null +++ b/test/vtable.cpp @@ -0,0 +1,256 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <boost/test/unit_test.hpp> +#include "test.hpp" + +#include <iostream> +#include <boost/sqlite.hpp> +#include <boost/unordered_map.hpp> +#include <boost/intrusive/list.hpp> + +#include <string> +#include <vector> + +using namespace boost; + + +BOOST_AUTO_TEST_SUITE(vtable_); + +struct del_info : sqlite3_index_info +{ + del_info() : sqlite3_index_info() + { + + } + + ~del_info() + { + if (needToFreeIdxStr) + sqlite3_free(idxStr); + } +}; + +struct trivial_struct +{ + trivial_struct() = default; + int i{1}, j{2}, k{3}; +}; + + + +struct simple_cursor final : sqlite::vtab::cursor<core::string_view> +{ + + simple_cursor(std::vector<std::string>::const_iterator itr, + std::vector<std::string>::const_iterator end) : itr(itr), end(end) {} + std::vector<std::string>::const_iterator itr, end; + + sqlite::result<void> next() {itr++; return {};} + sqlite::result<sqlite3_int64> row_id() {return *reinterpret_cast<sqlite3_int64*>(&itr);} + + sqlite::result<core::string_view> column(int i, bool /* no_change */) + { + if (i > 0) + throw_exception(std::out_of_range("column out of range")); + + return *itr; + } + + bool eof() noexcept {return itr == end;} +}; + +struct simple_table final : sqlite::vtab::table<simple_cursor> +{ + const char * declaration() + { + return R"(create table x(name text);)"; + } + + simple_table(std::vector<std::string> & names) : names(names) {} + std::vector<std::string> & names; + + sqlite::result<cursor_type> open() + { + return cursor_type{names.begin(), names.end()}; + } + +}; + +struct simple_test_impl final : sqlite::vtab::eponymous_module<simple_table> +{ + std::vector<std::string> names = {"ruben", "vinnie", "richard", "klemens"}; + + + + + sqlite::result<table_type> connect(sqlite::connection, + int /*argc*/, const char * const * /*argv*/) + { + return table_type{names}; + } + +}; + + +BOOST_AUTO_TEST_CASE(simple_reader) +{ + sqlite::connection conn(":memory:"); + auto & m = create_module(conn, "test_table", simple_test_impl{}); + + auto itr = m.names.begin(); + for (auto q : conn.query("select * from test_table ;")) + { + BOOST_CHECK(q.size() == 1); + BOOST_CHECK(q.at(0).get_text() == *itr++); + } + + m.names.emplace_back("marcelo"); + itr = m.names.begin(); + for (auto q : conn.query("select * from test_table ;")) + { + BOOST_CHECK(q.size() == 1); + BOOST_CHECK(q.at(0).get_text() == *itr++); + } + + BOOST_CHECK_THROW(conn.query("insert into test_table values('chris')"), boost::system::system_error); +} + +struct modifyable_table; + +struct modifyable_cursor final : sqlite::vtab::cursor<variant2::variant<std::int64_t, core::string_view>> +{ + using iterator = boost::unordered_map<std::int64_t, std::string>::const_iterator; + + modifyable_cursor(iterator itr, iterator end) : itr(itr), end(end) {} + + iterator itr, end; + + sqlite::result<void> next() noexcept {itr++; return {};} + sqlite::result<sqlite3_int64> row_id() noexcept {return itr->first;} + + sqlite::result<column_type> column(int i, bool /* no_change */) noexcept + { + switch (i) + { + case 0: return itr->first; + case 1: return itr->second; + default: + return sqlite::error(SQLITE_RANGE, sqlite::error_info("column out of range")); + + } + } + + bool eof() noexcept {return itr == end;} +}; + +struct modifyable_table final : + sqlite::vtab::table<modifyable_cursor>, + intrusive::list_base_hook<intrusive::link_mode<intrusive::auto_unlink> >, + sqlite::vtab::modifiable +{ + const char * declaration() + { + return R"(create table x(id integer primary key autoincrement, name text);)"; + } + + std::string name; + boost::unordered_map<std::int64_t, std::string> names; + + int last_index = 0; + + modifyable_table() = default; + modifyable_table(modifyable_table && lhs) + : name(std::move(lhs.name)), names(std::move(lhs.names)), last_index(lhs.last_index ) + { + this->swap_nodes(lhs); + } + + + sqlite::result<modifyable_cursor> open() + { + return modifyable_cursor{names.begin(), names.end()}; + } + + sqlite::result<void> delete_(sqlite::value key) + { + BOOST_CHECK(names.erase(key.get_int()) == 1); + return {}; + } + sqlite::result<sqlite_int64> insert(sqlite::value /*key*/, span<sqlite::value> values, + int /*on_conflict*/) + { + int id = values[0].is_null() ? last_index++ : values[0].get_int(); + auto itr = names.emplace(id, values[1].get_text()).first; + return itr->first; + } + sqlite::result<sqlite_int64> update(sqlite::value old_key, sqlite::value new_key, + span<sqlite::value> values, + int /*on_conflict*/) + { + if (new_key.get_int() != old_key.get_int()) + names.erase(old_key.get_int()); + names.insert_or_assign(new_key.get_int(), values[1].get_text()); + return 0u; + } +}; + +struct modifyable_test_impl final : sqlite::vtab::eponymous_module<modifyable_table> +{ + modifyable_test_impl() = default; + + intrusive::list<table_type, intrusive::constant_time_size<false>> list; + + sqlite::result<table_type> connect(sqlite::connection, + int /*argc*/, const char * const argv[]) noexcept + { + table_type tt{}; + tt.name.assign(argv[2]); + list.push_back(tt); + return sqlite::result<table_type>(std::move(tt)); + } +}; + + +BOOST_AUTO_TEST_CASE(modifyable_reader) +{ + sqlite::connection conn(":memory:"); + + auto & m = create_module(conn, "name_table", modifyable_test_impl{}); + conn.execute("create virtual table test_table USING name_table; "); + + BOOST_CHECK(m.list.size() == 1); + BOOST_CHECK(m.list.front().name == "test_table"); + BOOST_CHECK(m.list.front().names.empty()); + + conn.prepare("insert into test_table(name) values ($1), ($2), ($3), ($4);") + .execute(std::make_tuple("vinnie", "richard", "ruben", "peter")); + + auto & t = m.list.front(); + BOOST_CHECK(t.names.size() == 4); + BOOST_CHECK(t.names[0] == "vinnie"); + BOOST_CHECK(t.names[1] == "richard"); + BOOST_CHECK(t.names[2] == "ruben"); + BOOST_CHECK(t.names[3] == "peter"); + + conn.prepare("delete from test_table where name = $1;").execute(std::make_tuple("richard")); + + BOOST_CHECK(t.names.size() == 3); + BOOST_CHECK(t.names[0] == "vinnie"); + BOOST_CHECK(t.names[2] == "ruben"); + BOOST_CHECK(t.names[3] == "peter"); + + conn.prepare("update test_table set name = $1 where id = 0;").execute(std::make_tuple("richard")); + + BOOST_CHECK(t.names.size() == 3); + BOOST_CHECK(t.names[0] == "richard"); + BOOST_CHECK(t.names[2] == "ruben"); + BOOST_CHECK(t.names[3] == "peter"); +} + + +BOOST_AUTO_TEST_SUITE_END() |