|
|
@@ -34,6 +34,7 @@
|
|
|
|
#include <noto/schema.hpp>
|
|
|
|
#include <noto/schema.hpp>
|
|
|
|
#include <noto/transaction.hpp>
|
|
|
|
#include <noto/transaction.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(noto_asserts_follow_build_type) {
|
|
|
|
BOOST_AUTO_TEST_CASE(noto_asserts_follow_build_type) {
|
|
|
|
#if defined(NOTO_EXPECT_NDEBUG) && !defined(NDEBUG)
|
|
|
|
#if defined(NOTO_EXPECT_NDEBUG) && !defined(NDEBUG)
|
|
|
|
BOOST_FAIL("release-style builds must define NDEBUG");
|
|
|
|
BOOST_FAIL("release-style builds must define NDEBUG");
|
|
|
@@ -43,7 +44,6 @@ BOOST_AUTO_TEST_CASE(noto_asserts_follow_build_type) {
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
|
|
template <typename AWAITABLE>
|
|
|
|
template <typename AWAITABLE>
|
|
|
@@ -146,7 +146,6 @@ BOOST_AUTO_TEST_CASE(noto_status_defaults_to_ok) {
|
|
|
|
BOOST_AUTO_TEST_CASE(noto_result_carries_failure_status) {
|
|
|
|
BOOST_AUTO_TEST_CASE(noto_result_carries_failure_status) {
|
|
|
|
auto ok = std::expected<void, modulus::error>{};
|
|
|
|
auto ok = std::expected<void, modulus::error>{};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(ok.has_value()));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(ok.has_value()));
|
|
|
|
|
|
|
|
|
|
|
|
auto result = noto::detail::fail<std::int32_t>(noto::detail::error_invalid_argument, "bad value");
|
|
|
|
auto result = noto::detail::fail<std::int32_t>(noto::detail::error_invalid_argument, "bad value");
|
|
|
|
BOOST_REQUIRE(!result);
|
|
|
|
BOOST_REQUIRE(!result);
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(result.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(result.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
@@ -163,10 +162,8 @@ BOOST_AUTO_TEST_CASE(noto_status_maps_sqlite_errors) {
|
|
|
|
BOOST_AUTO_TEST_CASE(noto_config_validates_and_parses_json) {
|
|
|
|
BOOST_AUTO_TEST_CASE(noto_config_validates_and_parses_json) {
|
|
|
|
auto config = noto::connection_config{};
|
|
|
|
auto config = noto::connection_config{};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(noto::validate(config)));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(noto::validate(config)));
|
|
|
|
|
|
|
|
|
|
|
|
config.filename = "";
|
|
|
|
config.filename = "";
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(noto::validate(config)));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(noto::validate(config)));
|
|
|
|
|
|
|
|
|
|
|
|
auto value = boost::json::parse(R"json({
|
|
|
|
auto value = boost::json::parse(R"json({
|
|
|
|
"databases": [
|
|
|
|
"databases": [
|
|
|
|
{
|
|
|
|
{
|
|
|
@@ -196,27 +193,19 @@ BOOST_AUTO_TEST_CASE(noto_config_validates_and_parses_json) {
|
|
|
|
BOOST_TEST(round_trip->at(0).name == "main");
|
|
|
|
BOOST_TEST(round_trip->at(0).name == "main");
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(round_trip->at(0).mode) == static_cast<std::int32_t>(noto::open_mode::MEMORY));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(round_trip->at(0).mode) == static_cast<std::int32_t>(noto::open_mode::MEMORY));
|
|
|
|
BOOST_TEST(round_trip->at(0).pragmas.size() == 2U);
|
|
|
|
BOOST_TEST(round_trip->at(0).pragmas.size() == 2U);
|
|
|
|
|
|
|
|
|
|
|
|
auto bad = noto::parse_database_configs(boost::json::parse(R"json({"databases":[{"name":"bad","filename":":memory:","mode":"server"}]})json"));
|
|
|
|
auto bad = noto::parse_database_configs(boost::json::parse(R"json({"databases":[{"name":"bad","filename":":memory:","mode":"server"}]})json"));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(bad.has_value()));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(bad.has_value()));
|
|
|
|
|
|
|
|
|
|
|
|
auto missing_file = noto::parse_database_configs(boost::json::parse(R"json({"databases":[{"name":"bad"}]})json"));
|
|
|
|
auto missing_file = noto::parse_database_configs(boost::json::parse(R"json({"databases":[{"name":"bad"}]})json"));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(missing_file.has_value()));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(missing_file.has_value()));
|
|
|
|
|
|
|
|
|
|
|
|
config.filename = ":memory:";
|
|
|
|
config.filename = ":memory:";
|
|
|
|
config.busy_timeout = std::chrono::milliseconds{static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()) + 1};
|
|
|
|
config.busy_timeout = std::chrono::milliseconds{static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()) + 1};
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(noto::validate(config)));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(noto::validate(config)));
|
|
|
|
|
|
|
|
auto oversized_timeout_json = std::string{R"json({"databases":[{"name":"bad","filename":":memory:","busy_timeout_ms":)json"} + std::to_string(static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()) + 1) + R"json(}]})json";
|
|
|
|
auto oversized_timeout_json = std::string{R"json({"databases":[{"name":"bad","filename":":memory:","busy_timeout_ms":)json"}
|
|
|
|
|
|
|
|
+ std::to_string(static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()) + 1)
|
|
|
|
|
|
|
|
+ R"json(}]})json";
|
|
|
|
|
|
|
|
auto oversized_timeout = noto::parse_database_configs(boost::json::parse(oversized_timeout_json));
|
|
|
|
auto oversized_timeout = noto::parse_database_configs(boost::json::parse(oversized_timeout_json));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(oversized_timeout.has_value()));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(oversized_timeout.has_value()));
|
|
|
|
|
|
|
|
|
|
|
|
auto unknown_root = noto::parse_database_configs(boost::json::parse(R"json({"databases":[],"databasez":[]})json"));
|
|
|
|
auto unknown_root = noto::parse_database_configs(boost::json::parse(R"json({"databases":[],"databasez":[]})json"));
|
|
|
|
BOOST_REQUIRE(!unknown_root);
|
|
|
|
BOOST_REQUIRE(!unknown_root);
|
|
|
|
BOOST_TEST(unknown_root.error().describe().find("config root has unknown key: databasez") != std::string::npos);
|
|
|
|
BOOST_TEST(unknown_root.error().describe().find("config root has unknown key: databasez") != std::string::npos);
|
|
|
|
|
|
|
|
|
|
|
|
auto unknown_database = noto::parse_database_configs(boost::json::parse(R"json({"databases":[{"name":"bad","filename":":memory:","busy_timout_ms":250}]})json"));
|
|
|
|
auto unknown_database = noto::parse_database_configs(boost::json::parse(R"json({"databases":[{"name":"bad","filename":":memory:","busy_timout_ms":250}]})json"));
|
|
|
|
BOOST_REQUIRE(!unknown_database);
|
|
|
|
BOOST_REQUIRE(!unknown_database);
|
|
|
|
BOOST_TEST(unknown_database.error().describe().find("database has unknown key: busy_timout_ms") != std::string::npos);
|
|
|
|
BOOST_TEST(unknown_database.error().describe().find("database has unknown key: busy_timout_ms") != std::string::npos);
|
|
|
@@ -226,7 +215,6 @@ BOOST_AUTO_TEST_CASE(noto_schema_quotes_identifiers_and_builds_sqlite_table_sql)
|
|
|
|
auto quoted = noto::quote_identifier("with\"quote");
|
|
|
|
auto quoted = noto::quote_identifier("with\"quote");
|
|
|
|
BOOST_REQUIRE(quoted);
|
|
|
|
BOOST_REQUIRE(quoted);
|
|
|
|
BOOST_TEST(*quoted == "\"with\"\"quote\"");
|
|
|
|
BOOST_TEST(*quoted == "\"with\"\"quote\"");
|
|
|
|
|
|
|
|
|
|
|
|
auto email_type = noto::column_type::varchar(255);
|
|
|
|
auto email_type = noto::column_type::varchar(255);
|
|
|
|
BOOST_REQUIRE(email_type);
|
|
|
|
BOOST_REQUIRE(email_type);
|
|
|
|
auto sql = noto::create_table_sql({
|
|
|
|
auto sql = noto::create_table_sql({
|
|
|
@@ -257,7 +245,6 @@ BOOST_AUTO_TEST_CASE(noto_schema_quotes_identifiers_and_builds_sqlite_table_sql)
|
|
|
|
.strict = true,
|
|
|
|
.strict = true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_REQUIRE(sql);
|
|
|
|
BOOST_REQUIRE(sql);
|
|
|
|
BOOST_TEST(*sql == "CREATE TABLE IF NOT EXISTS \"users\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT, \"email\" TEXT NOT NULL, \"created_at\" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP) STRICT");
|
|
|
|
BOOST_TEST(*sql == "CREATE TABLE IF NOT EXISTS \"users\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT, \"email\" TEXT NOT NULL, \"created_at\" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP) STRICT");
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -335,7 +322,6 @@ BOOST_AUTO_TEST_CASE(noto_schema_builds_constraints_generated_columns_and_option
|
|
|
|
.without_rowid = true,
|
|
|
|
.without_rowid = true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_REQUIRE(sql);
|
|
|
|
BOOST_REQUIRE(sql);
|
|
|
|
BOOST_TEST(*sql == "CREATE TABLE IF NOT EXISTS \"accounts\" (\"tenant_id\" INTEGER NOT NULL, \"id\" INTEGER NOT NULL, \"email\" TEXT NOT NULL, \"email_key\" TEXT GENERATED ALWAYS AS (lower(\"email\")) STORED NOT NULL, PRIMARY KEY (\"tenant_id\", \"id\"), CONSTRAINT \"uq_accounts_email\" UNIQUE (\"tenant_id\", \"email_key\"), CONSTRAINT \"ck_accounts_email\" CHECK (\"email\" <> ''), CONSTRAINT \"fk_accounts_tenant\" FOREIGN KEY (\"tenant_id\") REFERENCES \"tenants\" (\"id\") ON DELETE CASCADE) WITHOUT ROWID");
|
|
|
|
BOOST_TEST(*sql == "CREATE TABLE IF NOT EXISTS \"accounts\" (\"tenant_id\" INTEGER NOT NULL, \"id\" INTEGER NOT NULL, \"email\" TEXT NOT NULL, \"email_key\" TEXT GENERATED ALWAYS AS (lower(\"email\")) STORED NOT NULL, PRIMARY KEY (\"tenant_id\", \"id\"), CONSTRAINT \"uq_accounts_email\" UNIQUE (\"tenant_id\", \"email_key\"), CONSTRAINT \"ck_accounts_email\" CHECK (\"email\" <> ''), CONSTRAINT \"fk_accounts_tenant\" FOREIGN KEY (\"tenant_id\") REFERENCES \"tenants\" (\"id\") ON DELETE CASCADE) WITHOUT ROWID");
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -346,14 +332,11 @@ BOOST_AUTO_TEST_CASE(noto_connection_executes_queries_and_prepared_statements) {
|
|
|
|
io.get_executor(),
|
|
|
|
io.get_executor(),
|
|
|
|
noto::memory_connection_config("test"),
|
|
|
|
noto::memory_connection_config("test"),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
auto connected = run(io, connection.async_connect());
|
|
|
|
auto connected = run(io, connection.async_connect());
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(connected));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(connected));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(connection.state()) == static_cast<std::int32_t>(noto::connection_state::CONNECTED));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(connection.state()) == static_cast<std::int32_t>(noto::connection_state::CONNECTED));
|
|
|
|
|
|
|
|
|
|
|
|
auto created = run(io, connection.async_execute("CREATE TABLE items(id INTEGER PRIMARY KEY, value TEXT NOT NULL)"));
|
|
|
|
auto created = run(io, connection.async_execute("CREATE TABLE items(id INTEGER PRIMARY KEY, value TEXT NOT NULL)"));
|
|
|
|
BOOST_REQUIRE(created);
|
|
|
|
BOOST_REQUIRE(created);
|
|
|
|
|
|
|
|
|
|
|
|
auto statement = run(io, connection.async_prepare("INSERT INTO items(value) VALUES(?1)"));
|
|
|
|
auto statement = run(io, connection.async_prepare("INSERT INTO items(value) VALUES(?1)"));
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
auto inserted = run(io, statement->async_execute({
|
|
|
|
auto inserted = run(io, statement->async_execute({
|
|
|
@@ -361,14 +344,12 @@ BOOST_AUTO_TEST_CASE(noto_connection_executes_queries_and_prepared_statements) {
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
BOOST_REQUIRE(inserted);
|
|
|
|
BOOST_REQUIRE(inserted);
|
|
|
|
BOOST_TEST(inserted->changes == 1);
|
|
|
|
BOOST_TEST(inserted->changes == 1);
|
|
|
|
|
|
|
|
|
|
|
|
auto selected = run(io, connection.async_execute("SELECT value FROM items WHERE id = ?1", {
|
|
|
|
auto selected = run(io, connection.async_execute("SELECT value FROM items WHERE id = ?1", {
|
|
|
|
noto::sqlite_value{inserted->last_insert_rowid},
|
|
|
|
noto::sqlite_value{inserted->last_insert_rowid},
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
BOOST_REQUIRE(selected);
|
|
|
|
BOOST_REQUIRE(selected);
|
|
|
|
BOOST_REQUIRE(selected->rows.size() == 1U);
|
|
|
|
BOOST_REQUIRE(selected->rows.size() == 1U);
|
|
|
|
BOOST_TEST(text_value(selected->rows.at(0).at(0)) == "alpha");
|
|
|
|
BOOST_TEST(text_value(selected->rows.at(0).at(0)) == "alpha");
|
|
|
|
|
|
|
|
|
|
|
|
auto finalized = run(io, statement->async_finalize());
|
|
|
|
auto finalized = run(io, statement->async_finalize());
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(finalized));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(finalized));
|
|
|
|
auto closed = run(io, connection.async_close());
|
|
|
|
auto closed = run(io, connection.async_close());
|
|
|
@@ -382,14 +363,12 @@ BOOST_AUTO_TEST_CASE(noto_close_with_live_statement_is_recoverable) {
|
|
|
|
noto::memory_connection_config("live_statement"),
|
|
|
|
noto::memory_connection_config("live_statement"),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
|
|
|
|
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT 1"));
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT 1"));
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
auto blocked = run(io, connection.async_close());
|
|
|
|
auto blocked = run(io, connection.async_close());
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(blocked));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(blocked));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(blocked.error().code()) == static_cast<std::int32_t>(noto::detail::error_unavailable));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(blocked.error().code()) == static_cast<std::int32_t>(noto::detail::error_unavailable));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(connection.state()) == static_cast<std::int32_t>(noto::connection_state::CONNECTED));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(connection.state()) == static_cast<std::int32_t>(noto::connection_state::CONNECTED));
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, statement->async_finalize())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, statement->async_finalize())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -403,14 +382,12 @@ BOOST_AUTO_TEST_CASE(noto_close_rolls_back_active_transaction) {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('transient')")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('transient')")));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto reopened = noto::connection{
|
|
|
|
auto reopened = noto::connection{
|
|
|
|
io.get_executor(),
|
|
|
|
io.get_executor(),
|
|
|
|
noto::file_connection_config(path),
|
|
|
|
noto::file_connection_config(path),
|
|
|
@@ -425,33 +402,29 @@ BOOST_AUTO_TEST_CASE(noto_close_rolls_back_active_transaction) {
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(noto_async_methods_capture_state_from_temporary_wrappers) {
|
|
|
|
BOOST_AUTO_TEST_CASE(noto_async_methods_capture_state_from_temporary_wrappers) {
|
|
|
|
auto io = boost::asio::io_context{};
|
|
|
|
auto io = boost::asio::io_context{};
|
|
|
|
|
|
|
|
|
|
|
|
auto connect_future = boost::asio::co_spawn(
|
|
|
|
auto connect_future = boost::asio::co_spawn(
|
|
|
|
io,
|
|
|
|
io,
|
|
|
|
noto::connection{
|
|
|
|
noto::connection{
|
|
|
|
io.get_executor(),
|
|
|
|
io.get_executor(),
|
|
|
|
noto::memory_connection_config("temporary_connection"),
|
|
|
|
noto::memory_connection_config("temporary_connection"),
|
|
|
|
}.async_connect(),
|
|
|
|
}
|
|
|
|
boost::asio::use_future
|
|
|
|
.async_connect(),
|
|
|
|
);
|
|
|
|
boost::asio::use_future);
|
|
|
|
io.run();
|
|
|
|
io.run();
|
|
|
|
io.restart();
|
|
|
|
io.restart();
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(connect_future.get()));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(connect_future.get()));
|
|
|
|
|
|
|
|
|
|
|
|
auto connection = noto::connection{
|
|
|
|
auto connection = noto::connection{
|
|
|
|
io.get_executor(),
|
|
|
|
io.get_executor(),
|
|
|
|
noto::memory_connection_config("temporary_wrappers"),
|
|
|
|
noto::memory_connection_config("temporary_wrappers"),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
|
|
|
|
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT 1"));
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT 1"));
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
auto finalize_future = boost::asio::co_spawn(io, noto::statement{std::move(*statement)}.async_finalize(), boost::asio::use_future);
|
|
|
|
auto finalize_future = boost::asio::co_spawn(io, noto::statement{std::move(*statement)}.async_finalize(), boost::asio::use_future);
|
|
|
|
io.run();
|
|
|
|
io.run();
|
|
|
|
io.restart();
|
|
|
|
io.restart();
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(finalize_future.get()));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(finalize_future.get()));
|
|
|
|
|
|
|
|
|
|
|
|
auto rolled_back = run(io, connection.async_begin_transaction());
|
|
|
|
auto rolled_back = run(io, connection.async_begin_transaction());
|
|
|
|
BOOST_REQUIRE(rolled_back);
|
|
|
|
BOOST_REQUIRE(rolled_back);
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('rolled back')")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('rolled back')")));
|
|
|
@@ -459,7 +432,6 @@ BOOST_AUTO_TEST_CASE(noto_async_methods_capture_state_from_temporary_wrappers) {
|
|
|
|
io.run();
|
|
|
|
io.run();
|
|
|
|
io.restart();
|
|
|
|
io.restart();
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(rollback_future.get()));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(rollback_future.get()));
|
|
|
|
|
|
|
|
|
|
|
|
auto committed = run(io, connection.async_begin_transaction());
|
|
|
|
auto committed = run(io, connection.async_begin_transaction());
|
|
|
|
BOOST_REQUIRE(committed);
|
|
|
|
BOOST_REQUIRE(committed);
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('committed')")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('committed')")));
|
|
|
@@ -467,7 +439,6 @@ BOOST_AUTO_TEST_CASE(noto_async_methods_capture_state_from_temporary_wrappers) {
|
|
|
|
io.run();
|
|
|
|
io.run();
|
|
|
|
io.restart();
|
|
|
|
io.restart();
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(commit_future.get()));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(commit_future.get()));
|
|
|
|
|
|
|
|
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM items"));
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM items"));
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 1);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 1);
|
|
|
@@ -483,22 +454,18 @@ BOOST_AUTO_TEST_CASE(noto_detached_transaction_rolls_back_when_commit_cannot_sta
|
|
|
|
};
|
|
|
|
};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value INTEGER NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value INTEGER NOT NULL)")));
|
|
|
|
|
|
|
|
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES(1), (2), (3), (4), (5)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES(1), (2), (3), (4), (5)")));
|
|
|
|
|
|
|
|
|
|
|
|
auto options = noto::export_options{};
|
|
|
|
auto options = noto::export_options{};
|
|
|
|
options.rows_per_yield = 1;
|
|
|
|
options.rows_per_yield = 1;
|
|
|
|
auto export_future = boost::asio::co_spawn(io, connection.async_export_sql_to_file(export_path, options), boost::asio::use_future);
|
|
|
|
auto export_future = boost::asio::co_spawn(io, connection.async_export_sql_to_file(export_path, options), boost::asio::use_future);
|
|
|
|
auto commit_future = boost::asio::co_spawn(io, noto::transaction{std::move(*transaction)}.async_commit(), boost::asio::use_future);
|
|
|
|
auto commit_future = boost::asio::co_spawn(io, noto::transaction{std::move(*transaction)}.async_commit(), boost::asio::use_future);
|
|
|
|
io.run();
|
|
|
|
io.run();
|
|
|
|
io.restart();
|
|
|
|
io.restart();
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(export_future.get()));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(export_future.get()));
|
|
|
|
auto commit = commit_future.get();
|
|
|
|
auto commit = commit_future.get();
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(commit.error().code()) == static_cast<std::int32_t>(noto::detail::error_unavailable));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(commit.error().code()) == static_cast<std::int32_t>(noto::detail::error_unavailable));
|
|
|
|
|
|
|
|
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM items"));
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM items"));
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
@@ -513,12 +480,10 @@ BOOST_AUTO_TEST_CASE(noto_statement_destructor_cleanup_allows_close) {
|
|
|
|
noto::memory_connection_config("statement_destructor"),
|
|
|
|
noto::memory_connection_config("statement_destructor"),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT 1"));
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT 1"));
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -530,7 +495,6 @@ BOOST_AUTO_TEST_CASE(noto_transactions_commit_and_rollback) {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
@@ -540,7 +504,6 @@ BOOST_AUTO_TEST_CASE(noto_transactions_commit_and_rollback) {
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM items"));
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM items"));
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
@@ -561,21 +524,17 @@ BOOST_AUTO_TEST_CASE(noto_failed_commit_remains_rollbackable) {
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE parents(id INTEGER PRIMARY KEY)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE parents(id INTEGER PRIMARY KEY)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE children(parent_id INTEGER NOT NULL, FOREIGN KEY(parent_id) REFERENCES parents(id) DEFERRABLE INITIALLY DEFERRED)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE children(parent_id INTEGER NOT NULL, FOREIGN KEY(parent_id) REFERENCES parents(id) DEFERRABLE INITIALLY DEFERRED)")));
|
|
|
|
|
|
|
|
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction(noto::transaction_mode::DEFERRED));
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction(noto::transaction_mode::DEFERRED));
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO children(parent_id) VALUES(42)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO children(parent_id) VALUES(42)")));
|
|
|
|
|
|
|
|
|
|
|
|
auto committed = run(io, transaction->async_commit());
|
|
|
|
auto committed = run(io, transaction->async_commit());
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(committed));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(committed));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(committed.error().code()) == static_cast<std::int32_t>(noto::detail::error_constraint_violation));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(committed.error().code()) == static_cast<std::int32_t>(noto::detail::error_constraint_violation));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(transaction->active()));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(transaction->active()));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, transaction->async_rollback())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, transaction->async_rollback())));
|
|
|
|
|
|
|
|
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM children"));
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM children"));
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
|
|
|
|
|
|
|
|
|
auto next = run(io, connection.async_begin_transaction());
|
|
|
|
auto next = run(io, connection.async_begin_transaction());
|
|
|
|
BOOST_REQUIRE(next);
|
|
|
|
BOOST_REQUIRE(next);
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, next->async_rollback())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, next->async_rollback())));
|
|
|
@@ -591,22 +550,18 @@ BOOST_AUTO_TEST_CASE(noto_detached_failed_commit_rolls_back) {
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE parents(id INTEGER PRIMARY KEY)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE parents(id INTEGER PRIMARY KEY)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE children(parent_id INTEGER NOT NULL, FOREIGN KEY(parent_id) REFERENCES parents(id) DEFERRABLE INITIALLY DEFERRED)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE children(parent_id INTEGER NOT NULL, FOREIGN KEY(parent_id) REFERENCES parents(id) DEFERRABLE INITIALLY DEFERRED)")));
|
|
|
|
|
|
|
|
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction(noto::transaction_mode::DEFERRED));
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction(noto::transaction_mode::DEFERRED));
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO children(parent_id) VALUES(42)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO children(parent_id) VALUES(42)")));
|
|
|
|
|
|
|
|
|
|
|
|
auto commit_future = boost::asio::co_spawn(io, noto::transaction{std::move(*transaction)}.async_commit(), boost::asio::use_future);
|
|
|
|
auto commit_future = boost::asio::co_spawn(io, noto::transaction{std::move(*transaction)}.async_commit(), boost::asio::use_future);
|
|
|
|
io.run();
|
|
|
|
io.run();
|
|
|
|
io.restart();
|
|
|
|
io.restart();
|
|
|
|
auto committed = commit_future.get();
|
|
|
|
auto committed = commit_future.get();
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(committed));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(committed));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(committed.error().code()) == static_cast<std::int32_t>(noto::detail::error_constraint_violation));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(committed.error().code()) == static_cast<std::int32_t>(noto::detail::error_constraint_violation));
|
|
|
|
|
|
|
|
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM children"));
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM children"));
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
|
|
|
|
|
|
|
|
|
auto next = run(io, connection.async_begin_transaction());
|
|
|
|
auto next = run(io, connection.async_begin_transaction());
|
|
|
|
BOOST_REQUIRE(next);
|
|
|
|
BOOST_REQUIRE(next);
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, next->async_rollback())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, next->async_rollback())));
|
|
|
@@ -621,13 +576,11 @@ BOOST_AUTO_TEST_CASE(noto_abandoned_transaction_rolls_back_before_next_operation
|
|
|
|
};
|
|
|
|
};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('abandoned')")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('abandoned')")));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM items"));
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM items"));
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
@@ -642,7 +595,6 @@ BOOST_AUTO_TEST_CASE(noto_transaction_callback_exceptions_return_status_and_roll
|
|
|
|
};
|
|
|
|
};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
|
|
|
|
|
|
|
|
auto status = run(io, connection.async_with_transaction(noto::transaction_mode::IMMEDIATE, [](noto::connection &active) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
|
|
|
|
auto status = run(io, connection.async_with_transaction(noto::transaction_mode::IMMEDIATE, [](noto::connection &active) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
|
|
|
|
auto inserted = co_await active.async_execute("INSERT INTO items(value) VALUES('temporary')");
|
|
|
|
auto inserted = co_await active.async_execute("INSERT INTO items(value) VALUES('temporary')");
|
|
|
|
if (!inserted) {
|
|
|
|
if (!inserted) {
|
|
|
@@ -652,7 +604,6 @@ BOOST_AUTO_TEST_CASE(noto_transaction_callback_exceptions_return_status_and_roll
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(status));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(status));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(status.error().code()) == static_cast<std::int32_t>(noto::detail::error_internal));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(status.error().code()) == static_cast<std::int32_t>(noto::detail::error_internal));
|
|
|
|
|
|
|
|
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM items"));
|
|
|
|
auto count = run(io, connection.async_execute("SELECT COUNT(*) FROM items"));
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 0);
|
|
|
@@ -667,19 +618,16 @@ BOOST_AUTO_TEST_CASE(noto_execute_options_limit_collected_results) {
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('one'), ('two')")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('one'), ('two')")));
|
|
|
|
|
|
|
|
|
|
|
|
auto row_options = noto::execute_options{};
|
|
|
|
auto row_options = noto::execute_options{};
|
|
|
|
row_options.max_rows = 1;
|
|
|
|
row_options.max_rows = 1;
|
|
|
|
auto too_many_rows = run(io, connection.async_execute("SELECT value FROM items ORDER BY value", {}, row_options));
|
|
|
|
auto too_many_rows = run(io, connection.async_execute("SELECT value FROM items ORDER BY value", {}, row_options));
|
|
|
|
BOOST_REQUIRE(!too_many_rows);
|
|
|
|
BOOST_REQUIRE(!too_many_rows);
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(too_many_rows.error().code()) == static_cast<std::int32_t>(noto::detail::error_resource_exhausted));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(too_many_rows.error().code()) == static_cast<std::int32_t>(noto::detail::error_resource_exhausted));
|
|
|
|
|
|
|
|
|
|
|
|
auto byte_options = noto::execute_options{};
|
|
|
|
auto byte_options = noto::execute_options{};
|
|
|
|
byte_options.max_result_bytes = 4;
|
|
|
|
byte_options.max_result_bytes = 4;
|
|
|
|
auto too_many_bytes = run(io, connection.async_execute("SELECT value FROM items WHERE value = 'one'", {}, byte_options));
|
|
|
|
auto too_many_bytes = run(io, connection.async_execute("SELECT value FROM items WHERE value = 'one'", {}, byte_options));
|
|
|
|
BOOST_REQUIRE(!too_many_bytes);
|
|
|
|
BOOST_REQUIRE(!too_many_bytes);
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(too_many_bytes.error().code()) == static_cast<std::int32_t>(noto::detail::error_resource_exhausted));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(too_many_bytes.error().code()) == static_cast<std::int32_t>(noto::detail::error_resource_exhausted));
|
|
|
|
|
|
|
|
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT value FROM items ORDER BY value"));
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT value FROM items ORDER BY value"));
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
auto statement_options = noto::execute_options{};
|
|
|
|
auto statement_options = noto::execute_options{};
|
|
|
@@ -700,7 +648,6 @@ BOOST_AUTO_TEST_CASE(noto_migrations_apply_validate_and_rollback) {
|
|
|
|
noto::memory_connection_config(),
|
|
|
|
noto::memory_connection_config(),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
|
|
|
|
|
|
|
|
auto migrations = std::vector<noto::migration>{
|
|
|
|
auto migrations = std::vector<noto::migration>{
|
|
|
|
{
|
|
|
|
{
|
|
|
|
.id = "001_create_table",
|
|
|
|
.id = "001_create_table",
|
|
|
@@ -717,18 +664,15 @@ BOOST_AUTO_TEST_CASE(noto_migrations_apply_validate_and_rollback) {
|
|
|
|
auto migrator = noto::migrator{connection};
|
|
|
|
auto migrator = noto::migrator{connection};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, migrator.async_migrate(migrations))));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, migrator.async_migrate(migrations))));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, migrator.async_migrate(migrations))));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, migrator.async_migrate(migrations))));
|
|
|
|
|
|
|
|
|
|
|
|
auto changed = migrations;
|
|
|
|
auto changed = migrations;
|
|
|
|
changed.at(0).checksum = "changed";
|
|
|
|
changed.at(0).checksum = "changed";
|
|
|
|
auto mismatch = run(io, migrator.async_migrate(changed));
|
|
|
|
auto mismatch = run(io, migrator.async_migrate(changed));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(mismatch));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(mismatch));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(mismatch.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(mismatch.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
|
|
|
|
|
|
|
|
auto records = run(io, migrator.async_applied_migrations());
|
|
|
|
auto records = run(io, migrator.async_applied_migrations());
|
|
|
|
BOOST_REQUIRE(records);
|
|
|
|
BOOST_REQUIRE(records);
|
|
|
|
BOOST_TEST(records->size() == 1U);
|
|
|
|
BOOST_TEST(records->size() == 1U);
|
|
|
|
BOOST_TEST(records->at(0).id == "001_create_table");
|
|
|
|
BOOST_TEST(records->at(0).id == "001_create_table");
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, migrator.async_rollback(migrations))));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, migrator.async_rollback(migrations))));
|
|
|
|
auto missing = run(io, connection.async_execute("SELECT name FROM sqlite_schema WHERE type = 'table' AND name = 'items'"));
|
|
|
|
auto missing = run(io, connection.async_execute("SELECT name FROM sqlite_schema WHERE type = 'table' AND name = 'items'"));
|
|
|
|
BOOST_REQUIRE(missing);
|
|
|
|
BOOST_REQUIRE(missing);
|
|
|
@@ -742,7 +686,6 @@ BOOST_AUTO_TEST_CASE(noto_backup_export_integrity_and_pragmas_work) {
|
|
|
|
auto failed_backup_path = temp_database_path("failed_backup");
|
|
|
|
auto failed_backup_path = temp_database_path("failed_backup");
|
|
|
|
auto oversized_retry_path = temp_database_path("oversized_retry");
|
|
|
|
auto oversized_retry_path = temp_database_path("oversized_retry");
|
|
|
|
auto export_path = temp_database_path("export_sql");
|
|
|
|
auto export_path = temp_database_path("export_sql");
|
|
|
|
|
|
|
|
|
|
|
|
auto connection = noto::connection{
|
|
|
|
auto connection = noto::connection{
|
|
|
|
io.get_executor(),
|
|
|
|
io.get_executor(),
|
|
|
|
noto::file_connection_config(source_path),
|
|
|
|
noto::file_connection_config(source_path),
|
|
|
@@ -759,14 +702,12 @@ BOOST_AUTO_TEST_CASE(noto_backup_export_integrity_and_pragmas_work) {
|
|
|
|
auto busy_timeout = run(io, connection.async_busy_timeout());
|
|
|
|
auto busy_timeout = run(io, connection.async_busy_timeout());
|
|
|
|
BOOST_REQUIRE(busy_timeout);
|
|
|
|
BOOST_REQUIRE(busy_timeout);
|
|
|
|
BOOST_TEST(*busy_timeout == std::chrono::milliseconds{37});
|
|
|
|
BOOST_TEST(*busy_timeout == std::chrono::milliseconds{37});
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(id INTEGER PRIMARY KEY, value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(id INTEGER PRIMARY KEY, value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('one'), ('two')")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('one'), ('two')")));
|
|
|
|
auto integrity = run(io, connection.async_integrity_check());
|
|
|
|
auto integrity = run(io, connection.async_integrity_check());
|
|
|
|
BOOST_REQUIRE(integrity);
|
|
|
|
BOOST_REQUIRE(integrity);
|
|
|
|
BOOST_REQUIRE(integrity->size() == 1U);
|
|
|
|
BOOST_REQUIRE(integrity->size() == 1U);
|
|
|
|
BOOST_TEST(integrity->at(0) == "ok");
|
|
|
|
BOOST_TEST(integrity->at(0) == "ok");
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_backup_to_file(backup_path))));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_backup_to_file(backup_path))));
|
|
|
|
auto exported = run(io, connection.async_export_sql());
|
|
|
|
auto exported = run(io, connection.async_export_sql());
|
|
|
|
BOOST_REQUIRE(exported);
|
|
|
|
BOOST_REQUIRE(exported);
|
|
|
@@ -776,7 +717,6 @@ BOOST_AUTO_TEST_CASE(noto_backup_export_integrity_and_pragmas_work) {
|
|
|
|
auto limited_export = run(io, connection.async_export_sql(limited_options));
|
|
|
|
auto limited_export = run(io, connection.async_export_sql(limited_options));
|
|
|
|
BOOST_REQUIRE(!limited_export);
|
|
|
|
BOOST_REQUIRE(!limited_export);
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(limited_export.error().code()) == static_cast<std::int32_t>(noto::detail::error_resource_exhausted));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(limited_export.error().code()) == static_cast<std::int32_t>(noto::detail::error_resource_exhausted));
|
|
|
|
|
|
|
|
|
|
|
|
auto *preserved = static_cast<sqlite3 *>(nullptr);
|
|
|
|
auto *preserved = static_cast<sqlite3 *>(nullptr);
|
|
|
|
BOOST_REQUIRE(sqlite3_open_v2(failed_backup_path.c_str(), &preserved, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite3_open_v2(failed_backup_path.c_str(), &preserved, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite_exec_ok(preserved, "CREATE TABLE preserved(value INTEGER); INSERT INTO preserved(value) VALUES(42)"));
|
|
|
|
BOOST_REQUIRE(sqlite_exec_ok(preserved, "CREATE TABLE preserved(value INTEGER); INSERT INTO preserved(value) VALUES(42)"));
|
|
|
@@ -789,17 +729,14 @@ BOOST_AUTO_TEST_CASE(noto_backup_export_integrity_and_pragmas_work) {
|
|
|
|
BOOST_REQUIRE(sqlite3_open_v2(failed_backup_path.c_str(), &preserved, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite3_open_v2(failed_backup_path.c_str(), &preserved, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_TEST(sqlite_scalar_int64(preserved, "SELECT value FROM preserved") == 42);
|
|
|
|
BOOST_TEST(sqlite_scalar_int64(preserved, "SELECT value FROM preserved") == 42);
|
|
|
|
sqlite3_close(preserved);
|
|
|
|
sqlite3_close(preserved);
|
|
|
|
|
|
|
|
|
|
|
|
auto oversized_retry = noto::backup_options{};
|
|
|
|
auto oversized_retry = noto::backup_options{};
|
|
|
|
oversized_retry.retry_delay = std::chrono::milliseconds{static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()) + 1};
|
|
|
|
oversized_retry.retry_delay = std::chrono::milliseconds{static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()) + 1};
|
|
|
|
auto failed_delay = run(io, connection.async_backup_to_file(oversized_retry_path, oversized_retry));
|
|
|
|
auto failed_delay = run(io, connection.async_backup_to_file(oversized_retry_path, oversized_retry));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(failed_delay.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(failed_delay.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
|
|
|
|
|
|
|
|
oversized_retry = noto::backup_options{};
|
|
|
|
oversized_retry = noto::backup_options{};
|
|
|
|
oversized_retry.retry_timeout = std::chrono::milliseconds{static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()) + 1};
|
|
|
|
oversized_retry.retry_timeout = std::chrono::milliseconds{static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()) + 1};
|
|
|
|
auto failed_timeout = run(io, connection.async_backup_to_file(oversized_retry_path, oversized_retry));
|
|
|
|
auto failed_timeout = run(io, connection.async_backup_to_file(oversized_retry_path, oversized_retry));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(failed_timeout.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(failed_timeout.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
|
|
|
|
|
|
|
|
write_file(export_path, "preserved export");
|
|
|
|
write_file(export_path, "preserved export");
|
|
|
|
auto limited_file_options = noto::export_options{};
|
|
|
|
auto limited_file_options = noto::export_options{};
|
|
|
|
limited_file_options.max_output_bytes = 16;
|
|
|
|
limited_file_options.max_output_bytes = 16;
|
|
|
@@ -807,11 +744,9 @@ BOOST_AUTO_TEST_CASE(noto_backup_export_integrity_and_pragmas_work) {
|
|
|
|
BOOST_REQUIRE(!limited_file_export);
|
|
|
|
BOOST_REQUIRE(!limited_file_export);
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(limited_file_export.error().code()) == static_cast<std::int32_t>(noto::detail::error_resource_exhausted));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(limited_file_export.error().code()) == static_cast<std::int32_t>(noto::detail::error_resource_exhausted));
|
|
|
|
BOOST_TEST(read_file(export_path) == "preserved export");
|
|
|
|
BOOST_TEST(read_file(export_path) == "preserved export");
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_export_sql_to_file(export_path))));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_export_sql_to_file(export_path))));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(std::filesystem::exists(export_path)));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(std::filesystem::exists(export_path)));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
|
|
|
|
|
|
|
|
auto restored = noto::connection{
|
|
|
|
auto restored = noto::connection{
|
|
|
|
io.get_executor(),
|
|
|
|
io.get_executor(),
|
|
|
|
noto::file_connection_config(backup_path),
|
|
|
|
noto::file_connection_config(backup_path),
|
|
|
@@ -821,7 +756,6 @@ BOOST_AUTO_TEST_CASE(noto_backup_export_integrity_and_pragmas_work) {
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_REQUIRE(count);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 2);
|
|
|
|
BOOST_TEST(int_value(count->rows.at(0).at(0)) == 2);
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, restored.async_close())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, restored.async_close())));
|
|
|
|
|
|
|
|
|
|
|
|
std::filesystem::remove(source_path);
|
|
|
|
std::filesystem::remove(source_path);
|
|
|
|
std::filesystem::remove(backup_path);
|
|
|
|
std::filesystem::remove(backup_path);
|
|
|
|
std::filesystem::remove(failed_backup_path);
|
|
|
|
std::filesystem::remove(failed_backup_path);
|
|
|
@@ -840,19 +774,16 @@ BOOST_AUTO_TEST_CASE(noto_backup_and_export_reject_live_database_destinations) {
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('safe')")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('safe')")));
|
|
|
|
|
|
|
|
|
|
|
|
auto self_backup = run(io, connection.async_backup_to_file(source_path));
|
|
|
|
auto self_backup = run(io, connection.async_backup_to_file(source_path));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(self_backup.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(self_backup.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
auto self_export = run(io, connection.async_export_sql_to_file(source_path));
|
|
|
|
auto self_export = run(io, connection.async_export_sql_to_file(source_path));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(self_export.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(self_export.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
|
|
|
|
|
|
|
|
for (auto suffix : std::array<std::string_view, 3>{"-wal", "-shm", "-journal"}) {
|
|
|
|
for (auto suffix : std::array<std::string_view, 3>{"-wal", "-shm", "-journal"}) {
|
|
|
|
auto backup = run(io, connection.async_backup_to_file(source_path + std::string{suffix}));
|
|
|
|
auto backup = run(io, connection.async_backup_to_file(source_path + std::string{suffix}));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(backup.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(backup.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
auto exported = run(io, connection.async_export_sql_to_file(source_path + std::string{suffix}));
|
|
|
|
auto exported = run(io, connection.async_export_sql_to_file(source_path + std::string{suffix}));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(exported.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(exported.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto error = std::error_code{};
|
|
|
|
auto error = std::error_code{};
|
|
|
|
std::filesystem::create_symlink(source_path, link_path, error);
|
|
|
|
std::filesystem::create_symlink(source_path, link_path, error);
|
|
|
|
if (!error) {
|
|
|
|
if (!error) {
|
|
|
@@ -861,7 +792,6 @@ BOOST_AUTO_TEST_CASE(noto_backup_and_export_reject_live_database_destinations) {
|
|
|
|
auto link_export = run(io, connection.async_export_sql_to_file(link_path));
|
|
|
|
auto link_export = run(io, connection.async_export_sql_to_file(link_path));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(link_export.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(link_export.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto rows = run(io, connection.async_execute("SELECT value FROM items"));
|
|
|
|
auto rows = run(io, connection.async_execute("SELECT value FROM items"));
|
|
|
|
BOOST_REQUIRE(rows);
|
|
|
|
BOOST_REQUIRE(rows);
|
|
|
|
BOOST_REQUIRE(rows->rows.size() == 1U);
|
|
|
|
BOOST_REQUIRE(rows->rows.size() == 1U);
|
|
|
@@ -875,9 +805,7 @@ BOOST_AUTO_TEST_CASE(noto_restore_to_file_is_atomic_and_reopens_connection) {
|
|
|
|
auto io = boost::asio::io_context{};
|
|
|
|
auto io = boost::asio::io_context{};
|
|
|
|
auto source_path = temp_database_path("restore_source");
|
|
|
|
auto source_path = temp_database_path("restore_source");
|
|
|
|
auto target_path = temp_database_path("restore_target");
|
|
|
|
auto target_path = temp_database_path("restore_target");
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_REQUIRE(create_sqlite_database(source_path, "CREATE TABLE items(value TEXT NOT NULL); INSERT INTO items(value) VALUES('new')"));
|
|
|
|
BOOST_REQUIRE(create_sqlite_database(source_path, "CREATE TABLE items(value TEXT NOT NULL); INSERT INTO items(value) VALUES('new')"));
|
|
|
|
|
|
|
|
|
|
|
|
auto connection = noto::connection{
|
|
|
|
auto connection = noto::connection{
|
|
|
|
io.get_executor(),
|
|
|
|
io.get_executor(),
|
|
|
|
noto::file_connection_config(target_path),
|
|
|
|
noto::file_connection_config(target_path),
|
|
|
@@ -890,13 +818,11 @@ BOOST_AUTO_TEST_CASE(noto_restore_to_file_is_atomic_and_reopens_connection) {
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_set_busy_timeout(std::chrono::milliseconds{37}))));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_set_busy_timeout(std::chrono::milliseconds{37}))));
|
|
|
|
write_file(target_path + "-journal", "stale rollback journal");
|
|
|
|
write_file(target_path + "-journal", "stale rollback journal");
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(std::filesystem::exists(target_path + "-journal")));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(std::filesystem::exists(target_path + "-journal")));
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_restore_from_file(source_path))));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_restore_from_file(source_path))));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(connection.state()) == static_cast<std::int32_t>(noto::connection_state::CONNECTED));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(connection.state()) == static_cast<std::int32_t>(noto::connection_state::CONNECTED));
|
|
|
|
auto timeout = run(io, connection.async_busy_timeout());
|
|
|
|
auto timeout = run(io, connection.async_busy_timeout());
|
|
|
|
BOOST_REQUIRE(timeout);
|
|
|
|
BOOST_REQUIRE(timeout);
|
|
|
|
BOOST_TEST(*timeout == std::chrono::milliseconds{37});
|
|
|
|
BOOST_TEST(*timeout == std::chrono::milliseconds{37});
|
|
|
|
|
|
|
|
|
|
|
|
auto rows = run(io, connection.async_execute("SELECT value FROM items"));
|
|
|
|
auto rows = run(io, connection.async_execute("SELECT value FROM items"));
|
|
|
|
BOOST_REQUIRE(rows);
|
|
|
|
BOOST_REQUIRE(rows);
|
|
|
|
BOOST_REQUIRE(rows->rows.size() == 1U);
|
|
|
|
BOOST_REQUIRE(rows->rows.size() == 1U);
|
|
|
@@ -905,12 +831,10 @@ BOOST_AUTO_TEST_CASE(noto_restore_to_file_is_atomic_and_reopens_connection) {
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(std::filesystem::exists(target_path + "-wal")));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(std::filesystem::exists(target_path + "-wal")));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(std::filesystem::exists(target_path + "-shm")));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(std::filesystem::exists(target_path + "-shm")));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(std::filesystem::exists(target_path + "-journal")));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(std::filesystem::exists(target_path + "-journal")));
|
|
|
|
|
|
|
|
|
|
|
|
auto *restored = static_cast<sqlite3 *>(nullptr);
|
|
|
|
auto *restored = static_cast<sqlite3 *>(nullptr);
|
|
|
|
BOOST_REQUIRE(sqlite3_open_v2(target_path.c_str(), &restored, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite3_open_v2(target_path.c_str(), &restored, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_TEST(sqlite_scalar_int64(restored, "SELECT COUNT(*) FROM items WHERE value = 'new'") == 1);
|
|
|
|
BOOST_TEST(sqlite_scalar_int64(restored, "SELECT COUNT(*) FROM items WHERE value = 'new'") == 1);
|
|
|
|
sqlite3_close(restored);
|
|
|
|
sqlite3_close(restored);
|
|
|
|
|
|
|
|
|
|
|
|
remove_database_files(source_path);
|
|
|
|
remove_database_files(source_path);
|
|
|
|
remove_database_files(target_path);
|
|
|
|
remove_database_files(target_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -919,10 +843,8 @@ BOOST_AUTO_TEST_CASE(noto_failed_restore_leaves_existing_database_intact) {
|
|
|
|
auto io = boost::asio::io_context{};
|
|
|
|
auto io = boost::asio::io_context{};
|
|
|
|
auto source_path = temp_database_path("restore_failure_source");
|
|
|
|
auto source_path = temp_database_path("restore_failure_source");
|
|
|
|
auto target_path = temp_database_path("restore_failure_target");
|
|
|
|
auto target_path = temp_database_path("restore_failure_target");
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_REQUIRE(create_sqlite_database(source_path, "CREATE TABLE items(value TEXT NOT NULL); INSERT INTO items(value) VALUES('new')"));
|
|
|
|
BOOST_REQUIRE(create_sqlite_database(source_path, "CREATE TABLE items(value TEXT NOT NULL); INSERT INTO items(value) VALUES('new')"));
|
|
|
|
BOOST_REQUIRE(create_sqlite_database(target_path, "CREATE TABLE items(value TEXT NOT NULL); INSERT INTO items(value) VALUES('old')"));
|
|
|
|
BOOST_REQUIRE(create_sqlite_database(target_path, "CREATE TABLE items(value TEXT NOT NULL); INSERT INTO items(value) VALUES('old')"));
|
|
|
|
|
|
|
|
|
|
|
|
auto connection = noto::connection{
|
|
|
|
auto connection = noto::connection{
|
|
|
|
io.get_executor(),
|
|
|
|
io.get_executor(),
|
|
|
|
noto::file_connection_config(target_path),
|
|
|
|
noto::file_connection_config(target_path),
|
|
|
@@ -932,13 +854,11 @@ BOOST_AUTO_TEST_CASE(noto_failed_restore_leaves_existing_database_intact) {
|
|
|
|
options.source_name = "missing";
|
|
|
|
options.source_name = "missing";
|
|
|
|
auto restored = run(io, connection.async_restore_from_file(source_path, options));
|
|
|
|
auto restored = run(io, connection.async_restore_from_file(source_path, options));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(restored));
|
|
|
|
BOOST_TEST(!static_cast<std::bool_t>(restored));
|
|
|
|
|
|
|
|
|
|
|
|
auto rows = run(io, connection.async_execute("SELECT value FROM items"));
|
|
|
|
auto rows = run(io, connection.async_execute("SELECT value FROM items"));
|
|
|
|
BOOST_REQUIRE(rows);
|
|
|
|
BOOST_REQUIRE(rows);
|
|
|
|
BOOST_REQUIRE(rows->rows.size() == 1U);
|
|
|
|
BOOST_REQUIRE(rows->rows.size() == 1U);
|
|
|
|
BOOST_TEST(text_value(rows->rows.at(0).at(0)) == "old");
|
|
|
|
BOOST_TEST(text_value(rows->rows.at(0).at(0)) == "old");
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
|
|
|
|
|
|
|
|
remove_database_files(source_path);
|
|
|
|
remove_database_files(source_path);
|
|
|
|
remove_database_files(target_path);
|
|
|
|
remove_database_files(target_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -947,10 +867,8 @@ BOOST_AUTO_TEST_CASE(noto_restore_rejects_unsupported_targets_and_active_state)
|
|
|
|
auto io = boost::asio::io_context{};
|
|
|
|
auto io = boost::asio::io_context{};
|
|
|
|
auto source_path = temp_database_path("restore_reject_source");
|
|
|
|
auto source_path = temp_database_path("restore_reject_source");
|
|
|
|
auto target_path = temp_database_path("restore_reject_target");
|
|
|
|
auto target_path = temp_database_path("restore_reject_target");
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_REQUIRE(create_sqlite_database(source_path, "CREATE TABLE items(value TEXT NOT NULL); INSERT INTO items(value) VALUES('new')"));
|
|
|
|
BOOST_REQUIRE(create_sqlite_database(source_path, "CREATE TABLE items(value TEXT NOT NULL); INSERT INTO items(value) VALUES('new')"));
|
|
|
|
BOOST_REQUIRE(create_sqlite_database(target_path, "CREATE TABLE items(value TEXT NOT NULL); INSERT INTO items(value) VALUES('old')"));
|
|
|
|
BOOST_REQUIRE(create_sqlite_database(target_path, "CREATE TABLE items(value TEXT NOT NULL); INSERT INTO items(value) VALUES('old')"));
|
|
|
|
|
|
|
|
|
|
|
|
auto memory = noto::connection{
|
|
|
|
auto memory = noto::connection{
|
|
|
|
io.get_executor(),
|
|
|
|
io.get_executor(),
|
|
|
|
noto::memory_connection_config("restore_reject_memory"),
|
|
|
|
noto::memory_connection_config("restore_reject_memory"),
|
|
|
@@ -959,31 +877,26 @@ BOOST_AUTO_TEST_CASE(noto_restore_rejects_unsupported_targets_and_active_state)
|
|
|
|
auto memory_restore = run(io, memory.async_restore_from_file(source_path));
|
|
|
|
auto memory_restore = run(io, memory.async_restore_from_file(source_path));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(memory_restore.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(memory_restore.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, memory.async_close())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, memory.async_close())));
|
|
|
|
|
|
|
|
|
|
|
|
auto connection = noto::connection{
|
|
|
|
auto connection = noto::connection{
|
|
|
|
io.get_executor(),
|
|
|
|
io.get_executor(),
|
|
|
|
noto::file_connection_config(target_path),
|
|
|
|
noto::file_connection_config(target_path),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
|
|
|
|
|
|
|
|
auto options = noto::backup_options{};
|
|
|
|
auto options = noto::backup_options{};
|
|
|
|
options.destination_name = "attached";
|
|
|
|
options.destination_name = "attached";
|
|
|
|
auto non_main = run(io, connection.async_restore_from_file(source_path, options));
|
|
|
|
auto non_main = run(io, connection.async_restore_from_file(source_path, options));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(non_main.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(non_main.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
|
|
|
|
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
auto transaction = run(io, connection.async_begin_transaction());
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
BOOST_REQUIRE(transaction);
|
|
|
|
auto active_transaction = run(io, connection.async_restore_from_file(source_path));
|
|
|
|
auto active_transaction = run(io, connection.async_restore_from_file(source_path));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(active_transaction.error().code()) == static_cast<std::int32_t>(noto::detail::error_unavailable));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(active_transaction.error().code()) == static_cast<std::int32_t>(noto::detail::error_unavailable));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, transaction->async_rollback())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, transaction->async_rollback())));
|
|
|
|
|
|
|
|
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT value FROM items"));
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT value FROM items"));
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
auto live_statement = run(io, connection.async_restore_from_file(source_path));
|
|
|
|
auto live_statement = run(io, connection.async_restore_from_file(source_path));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(live_statement.error().code()) == static_cast<std::int32_t>(noto::detail::error_unavailable));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(live_statement.error().code()) == static_cast<std::int32_t>(noto::detail::error_unavailable));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, statement->async_finalize())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, statement->async_finalize())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
|
|
|
|
|
|
|
|
remove_database_files(source_path);
|
|
|
|
remove_database_files(source_path);
|
|
|
|
remove_database_files(target_path);
|
|
|
|
remove_database_files(target_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -992,7 +905,6 @@ BOOST_AUTO_TEST_CASE(noto_backup_lock_timeout_and_statement_finalize_retry_work)
|
|
|
|
auto io = boost::asio::io_context{};
|
|
|
|
auto io = boost::asio::io_context{};
|
|
|
|
auto source_path = temp_database_path("locked_source");
|
|
|
|
auto source_path = temp_database_path("locked_source");
|
|
|
|
auto backup_path = temp_database_path("locked_backup");
|
|
|
|
auto backup_path = temp_database_path("locked_backup");
|
|
|
|
|
|
|
|
|
|
|
|
auto connection = noto::connection{
|
|
|
|
auto connection = noto::connection{
|
|
|
|
io.get_executor(),
|
|
|
|
io.get_executor(),
|
|
|
|
noto::file_connection_config(source_path),
|
|
|
|
noto::file_connection_config(source_path),
|
|
|
@@ -1002,25 +914,20 @@ BOOST_AUTO_TEST_CASE(noto_backup_lock_timeout_and_statement_finalize_retry_work)
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('one')")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('one')")));
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT value FROM items"));
|
|
|
|
auto statement = run(io, connection.async_prepare("SELECT value FROM items"));
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
BOOST_REQUIRE(statement);
|
|
|
|
|
|
|
|
|
|
|
|
auto *blocker = static_cast<sqlite3 *>(nullptr);
|
|
|
|
auto *blocker = static_cast<sqlite3 *>(nullptr);
|
|
|
|
BOOST_REQUIRE(sqlite3_open_v2(source_path.c_str(), &blocker, SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite3_open_v2(source_path.c_str(), &blocker, SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite_exec_ok(blocker, "BEGIN EXCLUSIVE"));
|
|
|
|
BOOST_REQUIRE(sqlite_exec_ok(blocker, "BEGIN EXCLUSIVE"));
|
|
|
|
|
|
|
|
|
|
|
|
auto options = noto::backup_options{};
|
|
|
|
auto options = noto::backup_options{};
|
|
|
|
options.pages_per_step = 1;
|
|
|
|
options.pages_per_step = 1;
|
|
|
|
options.retry_delay = std::chrono::milliseconds{1};
|
|
|
|
options.retry_delay = std::chrono::milliseconds{1};
|
|
|
|
options.retry_timeout = std::chrono::milliseconds{25};
|
|
|
|
options.retry_timeout = std::chrono::milliseconds{25};
|
|
|
|
|
|
|
|
|
|
|
|
auto backup_future = boost::asio::co_spawn(io, connection.async_backup_to_file(backup_path, options), boost::asio::use_future);
|
|
|
|
auto backup_future = boost::asio::co_spawn(io, connection.async_backup_to_file(backup_path, options), boost::asio::use_future);
|
|
|
|
auto finalize_future = boost::asio::co_spawn(io, statement->async_finalize(), boost::asio::use_future);
|
|
|
|
auto finalize_future = boost::asio::co_spawn(io, statement->async_finalize(), boost::asio::use_future);
|
|
|
|
io.run();
|
|
|
|
io.run();
|
|
|
|
io.restart();
|
|
|
|
io.restart();
|
|
|
|
|
|
|
|
|
|
|
|
auto backup = backup_future.get();
|
|
|
|
auto backup = backup_future.get();
|
|
|
|
auto finalized_while_busy = finalize_future.get();
|
|
|
|
auto finalized_while_busy = finalize_future.get();
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(backup.error().code()) == static_cast<std::int32_t>(noto::detail::error_timeout));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(backup.error().code()) == static_cast<std::int32_t>(noto::detail::error_timeout));
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(sqlite_exec_ok(blocker, "ROLLBACK")));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(sqlite_exec_ok(blocker, "ROLLBACK")));
|
|
|
|
sqlite3_close(blocker);
|
|
|
|
sqlite3_close(blocker);
|
|
|
|
if (!finalized_while_busy) {
|
|
|
|
if (!finalized_while_busy) {
|
|
|
@@ -1028,7 +935,6 @@ BOOST_AUTO_TEST_CASE(noto_backup_lock_timeout_and_statement_finalize_retry_work)
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, statement->async_finalize())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, statement->async_finalize())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
|
|
|
|
|
|
|
|
std::filesystem::remove(source_path);
|
|
|
|
std::filesystem::remove(source_path);
|
|
|
|
std::filesystem::remove(backup_path);
|
|
|
|
std::filesystem::remove(backup_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -1044,7 +950,6 @@ BOOST_AUTO_TEST_CASE(noto_export_preserves_nul_text_and_defers_triggers_until_af
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO audit(count) VALUES(0)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO audit(count) VALUES(0)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(value TEXT NOT NULL)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TRIGGER items_ai AFTER INSERT ON items BEGIN UPDATE audit SET count = count + 1; END")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TRIGGER items_ai AFTER INSERT ON items BEGIN UPDATE audit SET count = count + 1; END")));
|
|
|
|
|
|
|
|
|
|
|
|
auto insert = run(io, connection.async_prepare("INSERT INTO items(value) VALUES(?1)"));
|
|
|
|
auto insert = run(io, connection.async_prepare("INSERT INTO items(value) VALUES(?1)"));
|
|
|
|
BOOST_REQUIRE(insert);
|
|
|
|
BOOST_REQUIRE(insert);
|
|
|
|
auto embedded = std::string{
|
|
|
|
auto embedded = std::string{
|
|
|
@@ -1055,16 +960,13 @@ BOOST_AUTO_TEST_CASE(noto_export_preserves_nul_text_and_defers_triggers_until_af
|
|
|
|
noto::sqlite_value{embedded},
|
|
|
|
noto::sqlite_value{embedded},
|
|
|
|
})));
|
|
|
|
})));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, insert->async_finalize())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, insert->async_finalize())));
|
|
|
|
|
|
|
|
|
|
|
|
auto exported = run(io, connection.async_export_sql());
|
|
|
|
auto exported = run(io, connection.async_export_sql());
|
|
|
|
BOOST_REQUIRE(exported);
|
|
|
|
BOOST_REQUIRE(exported);
|
|
|
|
BOOST_TEST(exported->find("CAST(X'610062' AS TEXT)") != std::string::npos);
|
|
|
|
BOOST_TEST(exported->find("CAST(X'610062' AS TEXT)") != std::string::npos);
|
|
|
|
BOOST_TEST(exported->find("INSERT INTO \"items\"") < exported->find("CREATE TRIGGER"));
|
|
|
|
BOOST_TEST(exported->find("INSERT INTO \"items\"") < exported->find("CREATE TRIGGER"));
|
|
|
|
|
|
|
|
|
|
|
|
auto *restored = static_cast<sqlite3 *>(nullptr);
|
|
|
|
auto *restored = static_cast<sqlite3 *>(nullptr);
|
|
|
|
BOOST_REQUIRE(sqlite3_open(":memory:", &restored) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite3_open(":memory:", &restored) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite_exec_ok(restored, *exported));
|
|
|
|
BOOST_REQUIRE(sqlite_exec_ok(restored, *exported));
|
|
|
|
|
|
|
|
|
|
|
|
auto *statement = static_cast<sqlite3_stmt *>(nullptr);
|
|
|
|
auto *statement = static_cast<sqlite3_stmt *>(nullptr);
|
|
|
|
BOOST_REQUIRE(sqlite3_prepare_v2(restored, "SELECT value FROM items", -1, &statement, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite3_prepare_v2(restored, "SELECT value FROM items", -1, &statement, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite3_step(statement) == SQLITE_ROW);
|
|
|
|
BOOST_REQUIRE(sqlite3_step(statement) == SQLITE_ROW);
|
|
|
@@ -1076,7 +978,6 @@ BOOST_AUTO_TEST_CASE(noto_export_preserves_nul_text_and_defers_triggers_until_af
|
|
|
|
};
|
|
|
|
};
|
|
|
|
sqlite3_finalize(statement);
|
|
|
|
sqlite3_finalize(statement);
|
|
|
|
BOOST_TEST(restored_text == embedded);
|
|
|
|
BOOST_TEST(restored_text == embedded);
|
|
|
|
|
|
|
|
|
|
|
|
statement = nullptr;
|
|
|
|
statement = nullptr;
|
|
|
|
BOOST_REQUIRE(sqlite3_prepare_v2(restored, "SELECT count FROM audit", -1, &statement, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite3_prepare_v2(restored, "SELECT count FROM audit", -1, &statement, nullptr) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite3_step(statement) == SQLITE_ROW);
|
|
|
|
BOOST_REQUIRE(sqlite3_step(statement) == SQLITE_ROW);
|
|
|
@@ -1095,13 +996,11 @@ BOOST_AUTO_TEST_CASE(noto_export_preserves_autoincrement_sequence_and_generated_
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(id INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT NOT NULL, value_size INTEGER GENERATED ALWAYS AS (length(value)) STORED)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("CREATE TABLE items(id INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT NOT NULL, value_size INTEGER GENERATED ALWAYS AS (length(value)) STORED)")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('one'), ('two')")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("INSERT INTO items(value) VALUES('one'), ('two')")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("DELETE FROM items WHERE value = 'two'")));
|
|
|
|
BOOST_REQUIRE(run(io, connection.async_execute("DELETE FROM items WHERE value = 'two'")));
|
|
|
|
|
|
|
|
|
|
|
|
auto exported = run(io, connection.async_export_sql());
|
|
|
|
auto exported = run(io, connection.async_export_sql());
|
|
|
|
BOOST_REQUIRE(exported);
|
|
|
|
BOOST_REQUIRE(exported);
|
|
|
|
BOOST_TEST(exported->find("sqlite_sequence") != std::string::npos);
|
|
|
|
BOOST_TEST(exported->find("sqlite_sequence") != std::string::npos);
|
|
|
|
BOOST_TEST(exported->find("value_size") != std::string::npos);
|
|
|
|
BOOST_TEST(exported->find("value_size") != std::string::npos);
|
|
|
|
BOOST_TEST(exported->find("INSERT INTO \"items\"(\"id\", \"value\")") != std::string::npos);
|
|
|
|
BOOST_TEST(exported->find("INSERT INTO \"items\"(\"id\", \"value\")") != std::string::npos);
|
|
|
|
|
|
|
|
|
|
|
|
auto *restored = static_cast<sqlite3 *>(nullptr);
|
|
|
|
auto *restored = static_cast<sqlite3 *>(nullptr);
|
|
|
|
BOOST_REQUIRE(sqlite3_open(":memory:", &restored) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite3_open(":memory:", &restored) == SQLITE_OK);
|
|
|
|
BOOST_REQUIRE(sqlite_exec_ok(restored, *exported));
|
|
|
|
BOOST_REQUIRE(sqlite_exec_ok(restored, *exported));
|
|
|
@@ -1122,7 +1021,6 @@ BOOST_AUTO_TEST_CASE(noto_export_rejects_virtual_tables) {
|
|
|
|
if (!created) {
|
|
|
|
if (!created) {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto exported = run(io, connection.async_export_sql());
|
|
|
|
auto exported = run(io, connection.async_export_sql());
|
|
|
|
BOOST_REQUIRE(!exported);
|
|
|
|
BOOST_REQUIRE(!exported);
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(exported.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(exported.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
@@ -1135,7 +1033,6 @@ BOOST_AUTO_TEST_CASE(noto_file_helpers_reject_null_byte_paths) {
|
|
|
|
noto::memory_connection_config("path_validation"),
|
|
|
|
noto::memory_connection_config("path_validation"),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_connect())));
|
|
|
|
|
|
|
|
|
|
|
|
auto bad_path = std::string{
|
|
|
|
auto bad_path = std::string{
|
|
|
|
"bad\0path",
|
|
|
|
"bad\0path",
|
|
|
|
8,
|
|
|
|
8,
|
|
|
@@ -1144,6 +1041,5 @@ BOOST_AUTO_TEST_CASE(noto_file_helpers_reject_null_byte_paths) {
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(backup.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(backup.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
auto exported = run(io, connection.async_export_sql_to_file(bad_path));
|
|
|
|
auto exported = run(io, connection.async_export_sql_to_file(bad_path));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(exported.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
BOOST_TEST(static_cast<std::int32_t>(exported.error().code()) == static_cast<std::int32_t>(noto::detail::error_invalid_argument));
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
BOOST_TEST(static_cast<std::bool_t>(run(io, connection.async_close())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|