@@ -25,7 +25,6 @@
|
||||
namespace {
|
||||
|
||||
constexpr auto application_id = std::int64_t{0x56414D50};
|
||||
constexpr auto catalog_schema_version = std::int64_t{3};
|
||||
|
||||
auto connection_config(vamp_zero::database_config const &config) -> noto::connection_config {
|
||||
auto result = noto::file_connection_config(config.path, "product-vamp");
|
||||
@@ -89,6 +88,34 @@ auto count_value(noto::sqlite_value const &value) -> std::size_t {
|
||||
return count == nullptr || *count <= 0 ? 0U : static_cast<std::size_t>(*count);
|
||||
}
|
||||
|
||||
auto compatible_table(noto::connection &connection, std::string const &table) -> boost::asio::awaitable<std::bool_t> {
|
||||
auto columns = std::vector<std::string_view>{"id", "title", "composer", "key_signature", "source_path", "source_json", "measure_count", "total_pages"};
|
||||
if (table == "practice_exercises") {
|
||||
columns.push_back("exercise_id");
|
||||
}
|
||||
columns.push_back("part");
|
||||
auto parameters = std::vector<noto::sqlite_value>{noto::sqlite_value{table}};
|
||||
auto selected = co_await connection.async_execute("SELECT name, type, \"notnull\", pk FROM pragma_table_info(?1) "
|
||||
"WHERE EXISTS (SELECT 1 FROM pragma_table_list WHERE schema = 'main' AND name = ?1 AND type = 'table' AND strict = 1) ORDER BY cid",
|
||||
std::move(parameters));
|
||||
if (!selected || selected->rows.size() != columns.size()) {
|
||||
co_return false;
|
||||
}
|
||||
for (auto index = std::size_t{}; index < columns.size(); ++index) {
|
||||
auto const &row = selected->rows[index];
|
||||
if (row.size() != 4U) {
|
||||
co_return false;
|
||||
}
|
||||
auto required = std::get_if<std::int64_t>(&row[2].value);
|
||||
auto primary = std::get_if<std::int64_t>(&row[3].value);
|
||||
auto type = index == 6U || index == 7U ? "INTEGER" : "TEXT";
|
||||
if (text_value(row[0]) != columns[index] || text_value(row[1]) != type || required == nullptr || *required != 1 || primary == nullptr || *primary != (index == 0U ? 1 : 0)) {
|
||||
co_return false;
|
||||
}
|
||||
}
|
||||
co_return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
auto vamp_zero::async_validate_database(boost::asio::any_io_executor executor, vamp_zero::database_config config, std::bool_t integrity) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
|
||||
@@ -102,11 +129,15 @@ auto vamp_zero::async_validate_database(boost::asio::any_io_executor executor, v
|
||||
co_return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::PERSISTENCE, "cannot open catalog: " + config.path + "; rebuild or reinstall Product Vamp: " + connected.error().describe()));
|
||||
}
|
||||
auto application = co_await connection.async_application_id();
|
||||
auto version = co_await connection.async_user_version();
|
||||
auto status = std::expected<void, modulus::error>{};
|
||||
if (!application || !version || *application != application_id || *version != catalog_schema_version) {
|
||||
if (!application || *application != application_id) {
|
||||
status = std::unexpected(vamp_zero::make_error(vamp_zero::error_code::PERSISTENCE, "invalid or incompatible catalog: " + config.path + "; rebuild or reinstall Product Vamp"));
|
||||
}
|
||||
for (auto table : {"songs", "practice_exercises"}) {
|
||||
if (status && !(co_await compatible_table(connection, table))) {
|
||||
status = std::unexpected(vamp_zero::make_error(vamp_zero::error_code::PERSISTENCE, "incompatible catalog table " + std::string{table} + ": " + config.path + "; rebuild or reinstall Product Vamp"));
|
||||
}
|
||||
}
|
||||
if (status && integrity) {
|
||||
auto checked = co_await connection.async_execute("PRAGMA quick_check(1)");
|
||||
if (!checked || checked->rows.size() != 1U || checked->rows[0].size() != 1U || text_value(checked->rows[0][0]) != "ok") {
|
||||
|
||||
Reference in New Issue
Block a user