diff options
Diffstat (limited to 'src/djinterop/enginelibrary')
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 |