summaryrefslogtreecommitdiffstats
path: root/src/djinterop/enginelibrary
diff options
context:
space:
mode:
Diffstat (limited to 'src/djinterop/enginelibrary')
-rw-r--r--src/djinterop/enginelibrary/el_crate_impl.cpp23
-rw-r--r--src/djinterop/enginelibrary/el_database_impl.cpp56
-rw-r--r--src/djinterop/enginelibrary/el_database_impl.hpp2
-rw-r--r--src/djinterop/enginelibrary/el_storage.cpp101
-rw-r--r--src/djinterop/enginelibrary/el_storage.hpp31
-rw-r--r--src/djinterop/enginelibrary/el_track_impl.cpp5
-rw-r--r--src/djinterop/enginelibrary/schema.cpp95
-rw-r--r--src/djinterop/enginelibrary/schema/schema.cpp69
-rw-r--r--src/djinterop/enginelibrary/schema/schema.hpp59
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_11_1.cpp632
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_11_1.hpp42
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_13_0.cpp583
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_13_0.hpp (renamed from src/djinterop/enginelibrary/schema_1_6_0.hpp)23
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_13_1.cpp517
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_13_1.hpp41
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_13_2.cpp392
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_13_2.hpp (renamed from src/djinterop/enginelibrary/schema_1_7_1.hpp)21
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_15_0.cpp614
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_15_0.hpp (renamed from src/djinterop/enginelibrary/schema.hpp)29
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_17_0.cpp659
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_17_0.hpp45
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_18_0.cpp579
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_18_0.hpp41
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_6_0.cpp (renamed from src/djinterop/enginelibrary/schema_1_6_0.cpp)301
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_6_0.hpp59
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_7_1.cpp468
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_7_1.hpp42
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_9_1.cpp892
-rw-r--r--src/djinterop/enginelibrary/schema/schema_1_9_1.hpp55
-rw-r--r--src/djinterop/enginelibrary/schema/schema_validate_utils.hpp (renamed from src/djinterop/enginelibrary/schema_validate_utils.hpp)166
-rw-r--r--src/djinterop/enginelibrary/schema_1_7_1.cpp1033
31 files changed, 6278 insertions, 1397 deletions
diff --git a/src/djinterop/enginelibrary/el_crate_impl.cpp b/src/djinterop/enginelibrary/el_crate_impl.cpp
index 0567b02..c67b1b8 100644
--- a/src/djinterop/enginelibrary/el_crate_impl.cpp
+++ b/src/djinterop/enginelibrary/el_crate_impl.cpp
@@ -145,10 +145,25 @@ crate el_crate_impl::create_sub_crate(std::string name)
}
};
- storage_->db << "INSERT INTO Crate (title, path) VALUES (?, ?)"
- << name.data() << (path + name + ";");
-
- int64_t sub_id = storage_->db.last_insert_rowid();
+ int64_t sub_id;
+ if (storage_->version >= version_1_9_1)
+ {
+ // Newer schemas consider crates to be a kind of 'list', and so the
+ // `Crate` table has been replaced with a VIEW onto `List`. The main
+ // difference is that `List` does not have an integer primary key, so
+ // the new id will need to be determined in advance.
+ storage_->db << "SELECT IFNULL(MAX(id), 0) + 1 FROM Crate" >> sub_id;
+ storage_->db << "INSERT INTO Crate (id, title, path) VALUES (?, ?, ?)"
+ << sub_id << name.data() << (path + name + ";");
+ }
+ else
+ {
+ // Older schema versions have a dedicated table for crates that has
+ // an integer primary key, which will be filled automatically.
+ storage_->db << "INSERT INTO Crate (title, path) VALUES (?, ?)"
+ << name.data() << (path + name + ";");
+ sub_id = storage_->db.last_insert_rowid();
+ }
storage_->db << "INSERT INTO CrateParentList (crateOriginId, "
"crateParentId) VALUES (?, ?)"
diff --git a/src/djinterop/enginelibrary/el_database_impl.cpp b/src/djinterop/enginelibrary/el_database_impl.cpp
index bb82e24..acd7fda 100644
--- a/src/djinterop/enginelibrary/el_database_impl.cpp
+++ b/src/djinterop/enginelibrary/el_database_impl.cpp
@@ -21,9 +21,9 @@
#include <djinterop/enginelibrary/el_storage.hpp>
#include <djinterop/enginelibrary/el_track_impl.hpp>
#include <djinterop/enginelibrary/el_transaction_guard_impl.hpp>
-#include <djinterop/enginelibrary/schema.hpp>
-#include <djinterop/impl/util.hpp>
+#include <djinterop/enginelibrary/schema/schema.hpp>
#include <djinterop/transaction_guard.hpp>
+#include <djinterop/util.hpp>
namespace djinterop
{
@@ -50,14 +50,6 @@ void ensure_valid_crate_name(const std::string& name)
} // namespace
-el_database_impl::el_database_impl(std::string directory) :
- storage_{std::make_shared<el_storage>(std::move(directory))}
-{
- // TODO (haslersn): On construction, should we check that the database
- // version is supported? This would give more guarantees to a user that
- // obtains a database object.
-}
-
el_database_impl::el_database_impl(std::shared_ptr<el_storage> storage) :
storage_{std::move(storage)}
{
@@ -113,10 +105,25 @@ crate el_database_impl::create_root_crate(std::string name)
ensure_valid_crate_name(name);
el_transaction_guard_impl trans{storage_};
- storage_->db << "INSERT INTO Crate (title, path) VALUES (?, ?)"
- << name.data() << std::string{name} + ';';
-
- int64_t id = storage_->db.last_insert_rowid();
+ int64_t id;
+ if (storage_->version >= version_1_9_1)
+ {
+ // Newer schemas consider crates to be a kind of 'list', and so the
+ // `Crate` table has been replaced with a VIEW onto `List`. The main
+ // difference is that `List` does not have an integer primary key, so
+ // the new id will need to be determined in advance.
+ storage_->db << "SELECT IFNULL(MAX(id), 0) + 1 FROM Crate" >> id;
+ storage_->db << "INSERT INTO Crate (id, title, path) VALUES (?, ?, ?)"
+ << id << name.data() << std::string{name} + ';';
+ }
+ else
+ {
+ // Older schema versions have a dedicated table for crates that has
+ // an integer primary key, which will be filled automatically.
+ storage_->db << "INSERT INTO Crate (title, path) VALUES (?, ?)"
+ << name.data() << std::string{name} + ';';
+ id = storage_->db.last_insert_rowid();
+ }
storage_->db << "INSERT INTO CrateParentList (crateOriginId, "
"crateParentId) VALUES (?, ?)"
@@ -220,16 +227,14 @@ std::string el_database_impl::directory()
bool el_database_impl::is_supported()
{
- return djinterop::enginelibrary::is_supported(version());
+ return schema::is_supported(version());
}
void el_database_impl::verify()
{
- // Verify music schema
- verify_music_schema(storage_->db);
-
- // Verify performance schema
- verify_performance_schema(storage_->db);
+ auto schema_creator_validator =
+ schema::make_schema_creator_validator(version());
+ schema_creator_validator->verify(storage_->db);
}
void el_database_impl::remove_crate(crate cr)
@@ -322,11 +327,12 @@ std::string el_database_impl::uuid()
semantic_version el_database_impl::version()
{
- semantic_version version;
- storage_->db << "SELECT schemaVersionMajor, schemaVersionMinor, "
- "schemaVersionPatch FROM Information" >>
- std::tie(version.maj, version.min, version.pat);
- return version;
+ return storage_->version;
+}
+
+std::string el_database_impl::version_name()
+{
+ return storage_->schema_creator_validator->name();
}
} // namespace enginelibrary
diff --git a/src/djinterop/enginelibrary/el_database_impl.hpp b/src/djinterop/enginelibrary/el_database_impl.hpp
index 1feda91..2ba840f 100644
--- a/src/djinterop/enginelibrary/el_database_impl.hpp
+++ b/src/djinterop/enginelibrary/el_database_impl.hpp
@@ -29,7 +29,6 @@ namespace enginelibrary
class el_database_impl : public database_impl
{
public:
- el_database_impl(std::string directory);
el_database_impl(std::shared_ptr<el_storage> storage);
transaction_guard begin_transaction() override;
@@ -53,6 +52,7 @@ public:
const std::string& relative_path) override;
std::string uuid() override;
semantic_version version() override;
+ std::string version_name() override;
private:
std::shared_ptr<el_storage> storage_;
diff --git a/src/djinterop/enginelibrary/el_storage.cpp b/src/djinterop/enginelibrary/el_storage.cpp
index dff4639..bc4b253 100644
--- a/src/djinterop/enginelibrary/el_storage.cpp
+++ b/src/djinterop/enginelibrary/el_storage.cpp
@@ -15,51 +15,96 @@
along with libdjinterop. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <djinterop/enginelibrary/el_storage.hpp>
-#include <djinterop/enginelibrary/schema.hpp>
+#include "el_storage.hpp"
-namespace djinterop
+#include <djinterop/database.hpp>
+#include <djinterop/exceptions.hpp>
+
+#include "../util.hpp"
+#include "schema/schema.hpp"
+
+namespace djinterop::enginelibrary
{
-namespace enginelibrary
+namespace
{
-el_storage::el_storage(std::string directory)
- : directory{directory}, db{":memory:"}
+sqlite::database make_attached_db(const std::string& directory, bool must_exist)
{
- // TODO (mr-smidge): Throw custom database_not_found if files don't exist.
- // TODO (haslersn): Should we check that directory is an absolute path?
+ if (!dir_exists(directory))
+ {
+ if (must_exist)
+ {
+ throw database_not_found{directory};
+ }
+ else
+ {
+ // Note: only creates leaf directory, not entire tree.
+ create_dir(directory);
+ }
+ }
+
+ sqlite::database db{":memory:"};
db << "ATTACH ? as 'music'" << (directory + "/m.db");
db << "ATTACH ? as 'perfdata'" << (directory + "/p.db");
+ return db;
}
-bool el_storage::schema_version_supported(semantic_version schema_version)
-{
- return is_supported(schema_version);
-}
-
-void el_storage::create_and_validate_schema(semantic_version schema_version)
-{
- create_music_schema(db, schema_version);
- verify_music_schema(db);
- create_performance_schema(db, schema_version);
- verify_performance_schema(db);
-}
-
-bool el_storage::schema_created() const
+semantic_version get_version(sqlite::database& db)
{
+ // Check that the `Information` table has been created.
std::string sql =
"SELECT SUM(rows) FROM ("
" SELECT COUNT(*) AS rows "
- " FROM music.sqlite_master WHERE "
- " type = 'table' "
+ " FROM music.sqlite_master "
+ " WHERE name = 'Information' "
" UNION ALL "
" SELECT COUNT(*) AS rows "
" FROM perfdata.sqlite_master "
- " WHERE type = 'table'"
+ " WHERE name = 'Information' "
")";
int32_t table_count;
db << sql >> table_count;
- return table_count != 0;
+ if (table_count != 2)
+ {
+ throw database_inconsistency{
+ "Did not find an `Information` table for both the music and "
+ "performance databases"};
+ }
+
+ semantic_version music_version;
+ semantic_version perfdata_version;
+ db << "SELECT schemaVersionMajor, schemaVersionMinor, "
+ "schemaVersionPatch FROM music.Information" >>
+ std::tie(music_version.maj, music_version.min, music_version.pat);
+ db << "SELECT schemaVersionMajor, schemaVersionMinor, "
+ "schemaVersionPatch FROM music.Information" >>
+ std::tie(
+ perfdata_version.maj, perfdata_version.min, perfdata_version.pat);
+ if (music_version != perfdata_version)
+ {
+ throw database_inconsistency{
+ "The stated schema versions do not match between the music and "
+ "performance data databases!"};
+ }
+
+ return music_version;
+}
+
+} // anonymous namespace
+
+el_storage::el_storage(const std::string& directory) :
+ directory{directory}, db{make_attached_db(directory, true)},
+ version{get_version(db)},
+ schema_creator_validator{schema::make_schema_creator_validator(version)}
+{
+}
+
+el_storage::el_storage(const std::string& directory, semantic_version version) :
+ directory{directory}, db{make_attached_db(directory, false)},
+ version{version}, schema_creator_validator{
+ schema::make_schema_creator_validator(version)}
+{
+ // Create the desired schema on the new database.
+ schema_creator_validator->create(db);
}
-} // namespace enginelibrary
-} // namespace djinterop
+} // namespace djinterop::enginelibrary
diff --git a/src/djinterop/enginelibrary/el_storage.hpp b/src/djinterop/enginelibrary/el_storage.hpp
index 7e08a4e..8b9fdf6 100644
--- a/src/djinterop/enginelibrary/el_storage.hpp
+++ b/src/djinterop/enginelibrary/el_storage.hpp
@@ -20,34 +20,31 @@
#include <string>
#include <sqlite_modern_cpp.h>
+
#include <djinterop/semantic_version.hpp>
-namespace djinterop
-{
-namespace enginelibrary
+#include "schema/schema.hpp"
+
+namespace djinterop::enginelibrary
{
class el_storage
{
public:
- /// Returns a boolean indicating whether a given schema version is
- /// supported.
- static bool schema_version_supported(semantic_version schema_version);
-
- el_storage(std::string directory);
+ /// Construct by loading from an existing DB directory.
+ el_storage(const std::string& directory);
- /// Create and validate schema in an empty EL storage DB.
- void create_and_validate_schema(semantic_version schema_version);
-
- /// Returns a boolean indicating whether the EL DB has any schema.
- bool schema_created() const;
-
- std::string directory;
+ /// Construct by making a new, empty DB of a given version.
+ el_storage(const std::string& directory, semantic_version version);
+ const std::string directory;
// TODO - don't expose mutable SQLite connection - allow txn guard to be
// obtained from el_storage by other EL classes.
mutable sqlite::database db;
+
+ const semantic_version version;
+ std::unique_ptr<schema::schema_creator_validator> schema_creator_validator;
+
int64_t last_savepoint = 0;
};
-} // namespace enginelibrary
-} // namespace djinterop
+} // namespace djinterop::enginelibrary
diff --git a/src/djinterop/enginelibrary/el_track_impl.cpp b/src/djinterop/enginelibrary/el_track_impl.cpp
index 2067566..707253e 100644
--- a/src/djinterop/enginelibrary/el_track_impl.cpp
+++ b/src/djinterop/enginelibrary/el_track_impl.cpp
@@ -24,7 +24,7 @@
#include <djinterop/enginelibrary/el_database_impl.hpp>
#include <djinterop/enginelibrary/el_track_impl.hpp>
#include <djinterop/enginelibrary/el_transaction_guard_impl.hpp>
-#include <djinterop/impl/util.hpp>
+#include <djinterop/util.hpp>
namespace djinterop
{
@@ -59,8 +59,7 @@ stdx::optional<int64_t> to_timestamp(
return result;
}
-/// Calculate the quantisation number for waveforms, given a quantisation
-/// number.
+/// Calculate the quantisation number for waveforms, given a sample rate.
///
/// A few numbers written to the waveform performance data are rounded
/// to multiples of a particular "quantisation number", that is equal to
diff --git a/src/djinterop/enginelibrary/schema.cpp b/src/djinterop/enginelibrary/schema.cpp
deleted file mode 100644
index 7868267..0000000
--- a/src/djinterop/enginelibrary/schema.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- This file is part of libdjinterop.
-
- libdjinterop is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- libdjinterop is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with libdjinterop. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <string>
-
-#include <sqlite_modern_cpp.h>
-
-#include <djinterop/djinterop.hpp>
-#include <djinterop/enginelibrary/schema.hpp>
-#include <djinterop/enginelibrary/schema_1_6_0.hpp>
-#include <djinterop/enginelibrary/schema_1_7_1.hpp>
-
-namespace djinterop
-{
-namespace enginelibrary
-{
-template <typename F1, typename F2>
-static void dispatch(
- const semantic_version &version, F1 func_1_6_0, F2 func_1_7_1)
-{
- if (version == version_1_6_0)
- func_1_6_0();
- else if (version == version_1_7_1)
- func_1_7_1();
- else
- throw unsupported_database_version{version};
-}
-
-static semantic_version get_version(sqlite::database &db)
-{
- semantic_version version;
- db << "SELECT schemaVersionMajor, schemaVersionMinor, schemaVersionPatch "
- "FROM Information" >>
- std::tie(version.maj, version.min, version.pat);
- return version;
-}
-
-bool is_supported(const semantic_version &version)
-{
- // TODO - add support for DB schema version 1.9.1 (new "List" tables, old
- // ones as VIEWs)
- return (version == version_1_6_0 || version == version_1_7_1);
-}
-
-semantic_version verify_music_schema(sqlite::database &db)
-{
- auto version = get_version(db);
- dispatch(
- version, [&db] { verify_music_schema_1_6_0(db); },
- [&db] { verify_music_schema_1_7_1(db); });
-
- return version;
-}
-
-semantic_version verify_performance_schema(sqlite::database &db)
-{
- auto version = get_version(db);
- dispatch(
- version, [&db] { verify_performance_schema_1_6_0(db); },
- [&db] { verify_performance_schema_1_7_1(db); });
-
- return version;
-}
-
-void create_music_schema(sqlite::database &db, const semantic_version &version)
-{
- dispatch(
- version, [&db] { create_music_schema_1_6_0(db); },
- [&db] { create_music_schema_1_7_1(db); });
-}
-
-void create_performance_schema(
- sqlite::database &db, const semantic_version &version)
-{
- dispatch(
- version, [&db] { create_performance_schema_1_6_0(db); },
- [&db] { create_performance_schema_1_7_1(db); });
-}
-
-} // namespace enginelibrary
-} // namespace djinterop
diff --git a/src/djinterop/enginelibrary/schema/schema.cpp b/src/djinterop/enginelibrary/schema/schema.cpp
new file mode 100644
index 0000000..ede5297
--- /dev/null
+++ b/src/djinterop/enginelibrary/schema/schema.cpp
@@ -0,0 +1,69 @@
+/*
+ This file is part of libdjinterop.
+
+ libdjinterop is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libdjinterop is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libdjinterop. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <djinterop/djinterop.hpp>
+#include <djinterop/enginelibrary/schema/schema.hpp>
+#include <djinterop/enginelibrary/schema/schema_1_6_0.hpp>
+#include <djinterop/enginelibrary/schema/schema_1_7_1.hpp>
+#include <djinterop/enginelibrary/schema/schema_1_9_1.hpp>
+#include <djinterop/enginelibrary/schema/schema_1_11_1.hpp>
+#include <djinterop/enginelibrary/schema/schema_1_13_0.hpp>
+#include <djinterop/enginelibrary/schema/schema_1_13_1.hpp>
+#include <djinterop/enginelibrary/schema/schema_1_13_2.hpp>
+#include <djinterop/enginelibrary/schema/schema_1_15_0.hpp>
+#include <djinterop/enginelibrary/schema/schema_1_17_0.hpp>
+#include <djinterop/enginelibrary/schema/schema_1_18_0.hpp>
+
+namespace djinterop::enginelibrary::schema
+{
+std::unique_ptr<schema_creator_validator> make_schema_creator_validator(
+ const semantic_version& version)
+{
+ if (version == version_1_6_0)
+ return std::make_unique<schema_1_6_0>();
+ else if (version == version_1_7_1)
+ return std::make_unique<schema_1_7_1>();
+ else if (version == version_1_9_1)
+ return std::make_unique<schema_1_9_1>();
+ else if (version == version_1_11_1)
+ return std::make_unique<schema_1_11_1>();
+ else if (version == version_1_13_0)
+ return std::make_unique<schema_1_13_0>();
+ else if (version == version_1_13_1)
+ return std::make_unique<schema_1_13_1>();
+ else if (version == version_1_13_2)
+ return std::make_unique<schema_1_13_2>();
+ else if (version == version_1_15_0)
+ return std::make_unique<schema_1_15_0>();
+ else if (version == version_1_17_0)
+ return std::make_unique<schema_1_17_0>();
+ else if (version == version_1_18_0)
+ return std::make_unique<schema_1_18_0>();
+ else
+ throw unsupported_database_version{version};
+}
+
+bool is_supported(const semantic_version& version)
+{
+ return version == version_1_6_0 || version == version_1_7_1 ||
+ version == version_1_9_1 || version == version_1_11_1 ||
+ version == version_1_13_0 || version == version_1_13_1 ||
+ version == version_1_13_2 || version == version_1_15_0 ||
+ version == version_1_17_0 || version == version_1_18_0;
+}
+
+} // namespace djinterop::enginelibrary::schema
diff --git a/src/djinterop/enginelibrary/schema/schema.hpp b/src/djinterop/enginelibrary/schema/schema.hpp
new file mode 100644
index 0000000..9425dee
--- /dev/null
+++ b/src/djinterop/enginelibrary/schema/schema.hpp
@@ -0,0 +1,59 @@
+/*
+ This file is part of libdjinterop.
+
+ libdjinterop is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libdjinterop is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libdjinterop. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdexcept>
+#include <string>
+
+#include <sqlite_modern_cpp.h>
+
+#include <djinterop/semantic_version.hpp>
+
+namespace djinterop::enginelibrary::schema
+{
+class schema_creator_validator
+{
+public:
+ virtual ~schema_creator_validator() = default;
+ virtual std::string name() const = 0;
+
+ virtual void verify(sqlite::database& db) const
+ {
+ verify_music_schema(db);
+ verify_performance_schema(db);
+ }
+
+ virtual void create(sqlite::database& db)
+ {
+ create_music_schema(db);
+ create_performance_schema(db);
+ }
+
+protected:
+ virtual void verify_music_schema(sqlite::database& db) const = 0;
+ virtual void verify_performance_schema(sqlite::database& db) const = 0;
+ virtual void create_music_schema(sqlite::database& db) = 0;
+ virtual void create_performance_schema(sqlite::database& db) = 0;
+};
+
+bool is_supported(const semantic_version& version);
+
+std::unique_ptr<schema_creator_validator> make_schema_creator_validator(
+ const semantic_version& version);
+
+} // namespace djinterop::enginelibrary::schema
diff --git a/src/djinterop/enginelibrary/schema/schema_1_11_1.cpp b/src/djinterop/enginelibrary/schema/schema_1_11_1.cpp
new file mode 100644
index 0000000..2f35b3c
--- /dev/null
+++ b/src/djinterop/enginelibrary/schema/schema_1_11_1.cpp
@@ -0,0 +1,632 @@
+/*
+ This file is part of libdjinterop.
+
+ libdjinterop is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libdjinterop is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libdjinterop. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sqlite_modern_cpp.h>
+
+#include <djinterop/djinterop.hpp>
+#include <djinterop/enginelibrary/schema/schema_1_11_1.hpp>
+#include <djinterop/enginelibrary/schema/schema_validate_utils.hpp>
+#include <djinterop/util.hpp>
+
+namespace djinterop::enginelibrary::schema
+{
+void schema_1_11_1::verify_list(sqlite::database& db) const
+{
+ {
+ table_info cols{db, "music", "List"};
+ auto iter = cols.begin(), end = cols.end();
+ validate(iter, end, "id", "INTEGER", 0, "", 1);
+ ++iter;
+ validate(iter, end, "isFolder", "NUMERIC", 0, "", 0);
+ ++iter;
+ validate(iter, end, "path", "TEXT", 0, "", 0);
+ ++iter;
+ validate(iter, end, "title", "TEXT", 0, "", 0);
+ ++iter;
+ validate(iter, end, "trackCount", "INTEGER", 0, "", 0);
+ ++iter;
+ validate(iter, end, "type", "INTEGER", 0, "", 2);
+ ++iter;
+ validate_no_more(iter, end, "table_info", "List");
+ }
+ {
+ index_list indices{db, "music", "List"};
+ auto iter = indices.begin(), end = indices.end();
+ validate(iter, end, "index_List_id", 0, "c", 0);
+ ++iter;
+ validate(iter, end, "index_List_path", 0, "c", 0);
+ ++iter;
+ validate(iter, end, "index_List_type", 0, "c", 0);
+ ++iter;
+ validate(iter, end, "sqlite_autoindex_List_1", 1, "pk", 0);
+ ++iter;
+ validate_no_more(iter, end, "index_list", "List");
+ }
+ {
+ index_info ii{db, "music", "index_List_path"};
+ auto iter = ii.begin(), end = ii.end();
+ validate(iter, end, 0, "path");
+ ++iter;
+ validate_no_more(iter, end, "index_info", "index_List_path");
+ }
+ {
+ index_info ii{db, "music", "index_List_type"};
+ auto iter = ii.begin(), end = ii.end();
+ validate(iter, end, 0, "type");
+ ++iter;
+ validate_no_more(iter, end, "index_info", "index_List_type");
+ }
+ {
+ index_info ii{db, "music", "index_List_id"};
+ auto iter = ii.begin(), end = ii.end();
+ validate(iter, end, 0, "id");
+ ++iter;
+ validate_no_more(iter, end, "index_info", "index_List_id");
+ }
+ {
+ index_info ii{db, "music", "sqlite_autoindex_List_1"};
+ auto iter = ii.begin(), end = ii.end();
+ validate(iter, end, 0, "id");
+ ++iter;
+ validate(iter, end, 1, "type");
+ ++iter;
+ validate_no_more(iter, end, "index_info", "sqlite_autoindex_List_1");
+ }
+}
+
+void schema_1_11_1::verify_track(sqlite::database& db) const
+{
+ {
+ table_info cols{db, "music", "Track"};
+ auto iter = cols.begin(), end = cols.end();
+ validate(iter, end, "bitrate", "INTEGER", 0, "", 0);
+ ++iter;
+ validate(iter, end, "bpm", "INTEGER", 0, "", 0);
+ ++iter;
+ validate(iter, end, "bpmAnalyzed", "REAL", 0, "", 0);
+ ++iter;
+ validate(iter, end, "filename", "TEXT", 0, "", 0);
+ ++iter;
+ validate(iter, end, "id", "INTEGER", 0, "", 1);
+ ++iter;
+ validate(iter, end, "idAlbumArt", "INTEGER", 0, "", 0);
+ ++iter;
+ validate(iter, end, "idTrackInExternalDatabase", "INTEGER", 0, "", 0);
+ ++iter;
+ validate(iter, end, "isExternalTrack", "NUMERIC", 0, "", 0);
+ ++iter;
+ validate(iter, end, "length", "INTEGER", 0, "", 0);
+ ++iter;
+ validate(iter, end, "lengthCalculated", "INTEGER", 0, "", 0);
+ ++iter;
+ validate(iter, end, "path", "TEXT", 0, "", 0);
+ ++iter;
+ validate(iter, end, "pdbImportKey", "INTEGER", 0, "", 0);
+ ++iter;
+ validate(iter, end, "playOrder", "INTEGER", 0, "", 0);
+ ++iter;
+ validate(iter, end, "trackType", "INTEGER", 0, "", 0);
+ ++iter;
+ validate(iter, end, "uuidOfExternalDatabase", "TEXT", 0, "", 0);
+ ++iter;
+ validate(iter, end, "year", "INTEGER", 0, "", 0);
+ ++iter;
+ validate_no_more(iter, end, "table_info", "Track");
+ }
+ {
+ index_list indices{db, "music", "Track"};
+ auto iter = indices.begin(), end = indices.end();
+ validate(iter, end, "index_Track_filename", 0, "c", 0);
+ ++iter;
+ validate(iter, end, "index_Track_id", 0, "c", 0);
+ ++iter;
+ validate(iter, end, "index_Track_idAlbumArt", 0, "c", 0);
+ ++iter;
+ validate(iter, end, "index_Track_idTrackInExternalDatabase", 0, "c", 0);
+ ++iter;
+ validate(iter, end, "index_T