diff --git a/CMakeLists.txt b/CMakeLists.txt index b18d187..df0b876 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.10) project(libdjinterop - VERSION 0.22.1 + VERSION 0.23.0 DESCRIPTION "C++ library providing access to DJ record libraries") set(PROJECT_HOMEPAGE_URL "https://github.com/xsco/libdjinterop") @@ -62,6 +62,7 @@ add_library( src/djinterop/engine/schema/schema_2_21_0.cpp src/djinterop/engine/schema/schema_2_21_1.cpp src/djinterop/engine/schema/schema_2_21_2.cpp + src/djinterop/engine/schema/schema_3_0_0.cpp src/djinterop/engine/schema/schema.cpp src/djinterop/engine/v1/engine_crate_impl.cpp src/djinterop/engine/v1/engine_database_impl.cpp @@ -176,7 +177,7 @@ install(FILES DESTINATION "${DJINTEROP_INSTALL_INCLUDEDIR}") install(FILES include/djinterop/engine/engine.hpp - include/djinterop/engine/engine_version.hpp + include/djinterop/engine/engine_schema.hpp DESTINATION "${DJINTEROP_INSTALL_INCLUDEDIR}/engine") install(FILES include/djinterop/engine/v2/beat_data_blob.hpp diff --git a/README.md b/README.md index e840d51..3e25fc5 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,9 @@ What is supported: The library supports the following firmware and application versions: -* SC5000 Firmware from 1.0.3 to 3.4.0. +* SC5000 Firmware from 1.0.3 to 4.1.0. * Other players (e.g. SC6000/M) may work, but this is currently untested. -* Engine DJ Desktop (aka Engine Prime) from 1.0.1 to 3.4.0. +* Engine DJ Desktop (aka Engine Prime) from 1.0.1 to 4.1.0. What is not supported (yet): diff --git a/example/engine_library_v2_low_level.cpp b/example/engine_library_v2_low_level.cpp index 08b8c44..b9edab0 100644 --- a/example/engine_library_v2_low_level.cpp +++ b/example/engine_library_v2_low_level.cpp @@ -31,12 +31,13 @@ int main() auto dir = "Engine Library"; auto exists = ev2::engine_library::exists(dir); - auto library = exists ? ev2::engine_library{dir} - : ev2::engine_library::create(dir, e::latest_v2); + auto library = exists + ? ev2::engine_library{dir} + : ev2::engine_library::create(dir, e::latest_v2_schema); std::cout << (exists ? "Loaded" : "Created") << " engine library:" << std::endl; - std::cout << "Version: " << library.version().name << std::endl; + std::cout << "Version: " << library.schema() << std::endl; std::cout << "Path: " << dir << std::endl; std::cout << "UUID: " << library.information().get().uuid << std::endl; diff --git a/example/engine_prime.cpp b/example/engine_prime.cpp index 4b280a6..429a299 100644 --- a/example/engine_prime.cpp +++ b/example/engine_prime.cpp @@ -27,9 +27,9 @@ int main() using namespace std::string_literals; auto dir = "Engine Library"s; - auto version = e::latest; + auto schema = e::latest_schema; bool created; - auto db = e::create_or_load_database(dir, version, created); + auto db = e::create_or_load_database(dir, schema, created); std::cout << (created ? "Created " : "Loaded ") << "database in directory " << dir << std::endl; std::cout << "DB version is " << db.version_name() << std::endl; diff --git a/include/djinterop/engine/engine.hpp b/include/djinterop/engine/engine.hpp index bcd7509..d81c636 100644 --- a/include/djinterop/engine/engine.hpp +++ b/include/djinterop/engine/engine.hpp @@ -30,8 +30,7 @@ #include #include -#include -#include +#include #include #include @@ -42,214 +41,38 @@ struct beatgrid_marker; namespace engine { -/// Engine DJ OS 1.0.0. -constexpr const engine_version os_1_0_0{ - semantic_version{1, 0, 0}, engine_database_type::os, "Engine DJ OS 1.0.0", - semantic_version{1, 6, 0}}; - -/// Engine DJ OS 1.0.3. -constexpr const engine_version os_1_0_3{ - semantic_version{1, 0, 3}, engine_database_type::os, "Engine DJ OS 1.0.3", - semantic_version{1, 7, 1}}; - -/// Engine Prime 1.1.1. -constexpr const engine_version desktop_1_1_1{ - semantic_version{1, 1, 1}, engine_database_type::desktop, - "Engine Prime 1.1.1", semantic_version{1, 9, 1}}; - -/// Engine DJ OS 1.2.0. -constexpr const engine_version os_1_2_0{ - semantic_version{1, 2, 0}, engine_database_type::os, "Engine DJ OS 1.2.0", - semantic_version{1, 11, 1}}; - -/// Engine DJ OS 1.2.2. -constexpr const engine_version os_1_2_2{ - semantic_version{1, 2, 2}, engine_database_type::os, "Engine DJ OS 1.2.2", - semantic_version{1, 13, 0}}; - -/// Engine Prime 1.2.2. -constexpr const engine_version desktop_1_2_2{ - semantic_version{1, 2, 2}, engine_database_type::desktop, - "Engine Prime 1.2.2", semantic_version{1, 13, 1}}; - -/// Engine DJ OS 1.3.1. -constexpr const engine_version os_1_3_1{ - semantic_version{1, 3, 1}, engine_database_type::os, "Engine DJ OS 1.3.1", - semantic_version{1, 13, 2}}; - -/// Engine DJ OS 1.4.0. -constexpr const engine_version os_1_4_0{ - semantic_version{1, 4, 0}, engine_database_type::os, "Engine DJ OS 1.4.0", - semantic_version{1, 15, 0}}; - -/// Engine DJ OS 1.5.1/1.5.2. -constexpr const engine_version os_1_5_1{ - semantic_version{1, 5, 1}, engine_database_type::os, - "Engine DJ OS 1.5.1/1.5.2", semantic_version{1, 17, 0}}; - -/// Engine Prime 1.5.1/1.6.0/1.6.1. -constexpr const engine_version desktop_1_5_1{ - semantic_version{1, 5, 1}, engine_database_type::desktop, - "Engine Prime 1.5.1/1.6.0/1.6.1", semantic_version{1, 18, 0}}; - -/// Engine DJ OS 1.6.0/1.6.1/1.6.2. -constexpr const engine_version os_1_6_0{ - semantic_version{1, 6, 0}, engine_database_type::os, - "Engine DJ OS 1.6.0/1.6.1/1.6.2", semantic_version{1, 18, 0}}; - -/// Engine DJ Desktop 2.0.x, 2.1.x. -constexpr const engine_version desktop_2_0_0{ - semantic_version{2, 0, 0}, engine_database_type::desktop, - "Engine DJ Desktop 2.0.x to 2.1.x", semantic_version{2, 18, 0}}; - -/// Engine DJ OS 2.0.x, 2.1.x -constexpr const engine_version os_2_0_0{ - semantic_version{2, 0, 0}, engine_database_type::os, - "Engine DJ OS 2.0.x to 2.1.x", semantic_version{2, 18, 0}}; - -/// Engine DJ Desktop 2.2.x, 2.3.x -constexpr const engine_version desktop_2_2_0{ - semantic_version{2, 2, 0}, engine_database_type::desktop, - "Engine DJ Desktop 2.2.x to 2.3.x", semantic_version{2, 20, 1}}; - -/// Engine DJ OS 2.2.x, 2.3.x -constexpr const engine_version os_2_2_0{ - semantic_version{2, 2, 0}, engine_database_type::os, - "Engine DJ OS 2.2.x to 2.3.x", semantic_version{2, 20, 1}}; - -/// Engine DJ Desktop 2.4.0 -constexpr const engine_version desktop_2_4_0{ - semantic_version{2, 4, 0}, engine_database_type::desktop, - "Engine DJ Desktop 2.4.0", semantic_version{2, 20, 2}}; - -/// Engine DJ OS 2.4.0 -constexpr const engine_version os_2_4_0{ - semantic_version{2, 4, 0}, engine_database_type::os, "Engine DJ OS 2.4.0", - semantic_version{2, 20, 2}}; - -/// Engine DJ Desktop 3.0.0, 3.0.1 -constexpr const engine_version desktop_3_0_0{ - semantic_version{3, 0, 0}, engine_database_type::desktop, - "Engine DJ Desktop 3.0.0 to 3.0.1", semantic_version{2, 20, 3}}; - -/// Engine DJ OS 3.0.0, 3.0.1 -constexpr const engine_version os_3_0_0{ - semantic_version{3, 0, 0}, engine_database_type::os, - "Engine DJ OS 3.0.0 to 3.0.1", semantic_version{2, 20, 3}}; - -/// Engine DJ desktop 3.1.0 to 3.4.0 -constexpr const engine_version desktop_3_1_0{ - semantic_version{3, 1, 0}, engine_database_type::desktop, - "Engine DJ Desktop 3.1.0 to 3.4.0", semantic_version{2, 21, 0}}; - -/// Engine DJ OS 3.1.0 to 3.2.0 -constexpr const engine_version os_3_1_0{ - semantic_version{3, 1, 0}, engine_database_type::os, - "Engine DJ OS 3.1.0 to 3.4.0", semantic_version{2, 21, 0}}; - -/// Engine DJ desktop 4.0.0 -constexpr const engine_version desktop_4_0_0{ - semantic_version{4, 0, 0}, engine_database_type::desktop, - "Engine DJ Desktop 4.0.0", semantic_version{2, 21, 1}}; - -/// Engine DJ OS 4.0.0 -constexpr const engine_version os_4_0_0{ - semantic_version{4, 0, 0}, engine_database_type::os, - "Engine DJ OS 4.0.0", semantic_version{2, 21, 1}}; - -/// Engine DJ desktop 4.0.1 -constexpr const engine_version desktop_4_0_1{ - semantic_version{4, 0, 1}, engine_database_type::desktop, - "Engine DJ Desktop 4.0.1", semantic_version{2, 21, 2}}; - -/// Engine DJ OS 4.0.1 -constexpr const engine_version os_4_0_1{ - semantic_version{4, 0, 1}, engine_database_type::os, - "Engine DJ OS 4.0.1", semantic_version{2, 21, 2}}; - -/// Set of available versions. -constexpr const std::array all_versions{ - os_1_0_0, os_1_0_3, desktop_1_1_1, os_1_2_0, os_1_2_2, - desktop_1_2_2, os_1_3_1, os_1_4_0, os_1_5_1, desktop_1_5_1, - os_1_6_0, desktop_2_0_0, os_2_0_0, desktop_2_2_0, os_2_2_0, - desktop_2_4_0, os_2_4_0, desktop_3_0_0, os_3_0_0, desktop_3_1_0, - os_3_1_0, desktop_4_0_0, os_4_0_0, desktop_4_0_1, os_4_0_1}; - -/// Set of available V1 versions. -constexpr const std::array all_v1_versions{ - os_1_0_0, os_1_0_3, desktop_1_1_1, os_1_2_0, os_1_2_2, desktop_1_2_2, - os_1_3_1, os_1_4_0, os_1_5_1, desktop_1_5_1, os_1_6_0}; - -/// Set of available V2 versions. -constexpr const std::array all_v2_versions{ - desktop_2_0_0, os_2_0_0, desktop_2_2_0, os_2_2_0, desktop_2_4_0, - os_2_4_0, desktop_3_0_0, os_3_0_0, desktop_3_1_0, os_3_1_0, - desktop_4_0_0, os_4_0_0, desktop_4_0_1, os_4_0_1}; - -/// The most recent version supported by the library. -constexpr engine_version latest = os_4_0_1; - -/// The most recent V2 version supported by the library. -constexpr engine_version latest_v2 = os_4_0_1; - -/// The most recent OS-type version supported by the library. -constexpr engine_version latest_os = os_4_0_1; - namespace standard_pad_colors { -constexpr const pad_color pad_1{0xEA, 0xC5, 0x32, 0xFF}; -constexpr const pad_color pad_2{0xEA, 0x8F, 0x32, 0xFF}; -constexpr const pad_color pad_3{0xB8, 0x55, 0xBF, 0xFF}; -constexpr const pad_color pad_4{0xBA, 0x2A, 0x41, 0xFF}; -constexpr const pad_color pad_5{0x86, 0xC6, 0x4B, 0xFF}; -constexpr const pad_color pad_6{0x20, 0xC6, 0x7C, 0xFF}; -constexpr const pad_color pad_7{0x00, 0xA8, 0xB1, 0xFF}; -constexpr const pad_color pad_8{0x15, 0x8E, 0xE2, 0xFF}; - -constexpr const std::array pads{pad_1, pad_2, pad_3, pad_4, - pad_5, pad_6, pad_7, pad_8}; +constexpr pad_color pad_1{0xEA, 0xC5, 0x32, 0xFF}; +constexpr pad_color pad_2{0xEA, 0x8F, 0x32, 0xFF}; +constexpr pad_color pad_3{0xB8, 0x55, 0xBF, 0xFF}; +constexpr pad_color pad_4{0xBA, 0x2A, 0x41, 0xFF}; +constexpr pad_color pad_5{0x86, 0xC6, 0x4B, 0xFF}; +constexpr pad_color pad_6{0x20, 0xC6, 0x7C, 0xFF}; +constexpr pad_color pad_7{0x00, 0xA8, 0xB1, 0xFF}; +constexpr pad_color pad_8{0x15, 0x8E, 0xE2, 0xFF}; + +constexpr std::array pads{pad_1, pad_2, pad_3, pad_4, + pad_5, pad_6, pad_7, pad_8}; } // namespace standard_pad_colors constexpr const char* default_database_dir_name = "Engine Library"; -/// The `unsupported_engine_database` exception is thrown when an Engine -/// database schema version is encountered that is not yet supported by this -/// version of the library. -class DJINTEROP_PUBLIC unsupported_engine_database : public unsupported_database -{ -public: - explicit unsupported_engine_database( - const semantic_version& version) noexcept : - unsupported_database{ - std::string{"Unsupported database version " + to_string(version)}}, - version{version} - { - } - - explicit unsupported_engine_database( - const std::string& what_arg, const semantic_version& version) noexcept : - unsupported_database{what_arg}, version{version} - { - } - - const semantic_version version; -}; - /// Creates a new, empty database in a directory using the version provided. /// /// By convention, the last part of the directory path is "Engine Library". If /// a database already exists in the target directory, an exception will be /// thrown. database DJINTEROP_PUBLIC create_database( - const std::string& directory, const engine_version& version = latest); + const std::string& directory, const engine_schema& schema = latest_schema); /// Creates a new temporary database. /// /// Any changes made to the database will not be persisted anywhere, and will /// be lost upon destruction of the returned variable. database DJINTEROP_PUBLIC -create_temporary_database(const engine_version& version = latest); +create_temporary_database(const engine_schema& schema = latest_schema); /// Creates a new database from a set of SQL scripts. /// @@ -260,12 +83,12 @@ create_temporary_database(const engine_version& version = latest); /// /// \param db_directory Directory in which to create database. /// \param script_directory Directory containing scripts. -/// \param loaded_version Output reference parameter indicating the version of +/// \param loaded_schema Output reference parameter indicating the version of /// the created database. /// \return Returns the created database. database DJINTEROP_PUBLIC create_database_from_scripts( const std::string& db_directory, const std::string& script_directory, - engine_version& loaded_version); + engine_schema& loaded_schema); /// Creates a new database from a set of SQL scripts. /// @@ -280,7 +103,7 @@ database DJINTEROP_PUBLIC create_database_from_scripts( inline database DJINTEROP_PUBLIC create_database_from_scripts( const std::string& db_directory, const std::string& script_directory) { - engine_version unused{}; + engine_schema unused{}; return create_database_from_scripts(db_directory, script_directory, unused); } @@ -290,16 +113,16 @@ inline database DJINTEROP_PUBLIC create_database_from_scripts( /// it will be created at the specified schema version. /// /// \param directory Directory to create or load in. -/// \param version Version to create, if the database does not already exist. +/// \param schema Schema version, if the database does not already exist. /// \param created Output reference parameter indicating whether a new database /// was created or not. -/// \param loaded_version Output reference parameter indicating the version of -/// any existing database that was loaded. The value is -/// not defined if a new database was created. +/// \param loaded_schema Output reference parameter indicating the schema +/// version of any existing database that was loaded. The value is not defined +/// if a new database was created. /// \return Returns the created or loaded database. database DJINTEROP_PUBLIC create_or_load_database( - const std::string& directory, const engine_version& version, bool& created, - engine_version& loaded_version); + const std::string& directory, const engine_schema& schema, bool& created, + engine_schema& loaded_schema); /// Create or load an Engine Library database in a given directory. /// @@ -307,15 +130,15 @@ database DJINTEROP_PUBLIC create_or_load_database( /// it will be created at the specified schema version. /// /// \param directory Directory to create or load in. -/// \param version Version to create, if the database does not already exist. +/// \param schema Schema version, if the database does not already exist. /// \param created Output reference parameter indicating whether a new database /// was created or not. /// \return Returns the created or loaded database. inline database DJINTEROP_PUBLIC create_or_load_database( - const std::string& directory, const engine_version& version, bool& created) + const std::string& directory, const engine_schema& schema, bool& created) { - engine_version unused{}; - return create_or_load_database(directory, version, created, unused); + engine_schema unused{}; + return create_or_load_database(directory, schema, created, unused); } /// Returns a boolean indicating whether an Engine Library already exists in a @@ -325,11 +148,11 @@ bool DJINTEROP_PUBLIC database_exists(const std::string& directory); /// Loads an Engine Library database from a given directory. /// /// \param directory Directory to load from. -/// \param loaded_version Output reference parameter indicating the version of +/// \param loaded_schema Output reference parameter indicating the version of /// the loaded database. /// \return Returns the loaded database. database DJINTEROP_PUBLIC -load_database(const std::string& directory, engine_version& loaded_version); +load_database(const std::string& directory, engine_schema& loaded_schema); /// Loads an Engine Library database from a given directory. /// @@ -337,7 +160,7 @@ load_database(const std::string& directory, engine_version& loaded_version); /// \return Returns the loaded database. inline database DJINTEROP_PUBLIC load_database(const std::string& directory) { - engine_version unused{}; + engine_schema unused{}; return load_database(directory, unused); } diff --git a/include/djinterop/engine/engine_schema.hpp b/include/djinterop/engine/engine_schema.hpp new file mode 100644 index 0000000..a14f485 --- /dev/null +++ b/include/djinterop/engine/engine_schema.hpp @@ -0,0 +1,168 @@ +/* + 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 . + */ + +#pragma once +#ifndef DJINTEROP_ENGINE_ENGINE_SCHEMA_HPP +#define DJINTEROP_ENGINE_ENGINE_SCHEMA_HPP + +#if __cplusplus < 201703L +#error This library needs at least a C++17 compliant compiler +#endif + +#include +#include +#include + +namespace djinterop::engine +{ +/// Enumeration of Engine database schemas. +enum class engine_schema +{ + schema_1_6_0, + schema_1_7_1, + schema_1_9_1, + schema_1_11_1, + schema_1_13_0, + schema_1_13_1, + schema_1_13_2, + schema_1_15_0, + schema_1_17_0, + schema_1_18_0_desktop, + schema_1_18_0_os, + + schema_2_18_0, + schema_2_20_1, + schema_2_20_2, + schema_2_20_3, + schema_2_21_0, + schema_2_21_1, + schema_2_21_2, + + schema_3_0_0, +}; + +/// Set of supported schema versions. +constexpr std::array supported_schemas{ + engine_schema::schema_1_6_0, engine_schema::schema_1_7_1, + engine_schema::schema_1_9_1, engine_schema::schema_1_11_1, + engine_schema::schema_1_13_0, engine_schema::schema_1_13_1, + engine_schema::schema_1_13_2, engine_schema::schema_1_15_0, + engine_schema::schema_1_17_0, engine_schema::schema_1_18_0_desktop, + engine_schema::schema_1_18_0_os, engine_schema::schema_2_18_0, + engine_schema::schema_2_20_1, engine_schema::schema_2_20_2, + engine_schema::schema_2_20_3, engine_schema::schema_2_21_0, + engine_schema::schema_2_21_1, engine_schema::schema_2_21_2}; + +/// Set of supported legacy schema versions. +constexpr std::array supported_v1_schemas{ + engine_schema::schema_1_6_0, engine_schema::schema_1_7_1, + engine_schema::schema_1_9_1, engine_schema::schema_1_11_1, + engine_schema::schema_1_13_0, engine_schema::schema_1_13_1, + engine_schema::schema_1_13_2, engine_schema::schema_1_15_0, + engine_schema::schema_1_17_0, engine_schema::schema_1_18_0_desktop, + engine_schema::schema_1_18_0_os}; + +/// Set of supported "Database2" schema versions. +constexpr std::array supported_v2_schemas{ + engine_schema::schema_2_18_0, engine_schema::schema_2_20_1, + engine_schema::schema_2_20_2, engine_schema::schema_2_20_3, + engine_schema::schema_2_21_0, engine_schema::schema_2_21_1, + engine_schema::schema_2_21_2}; + +/// The most recent schema version supported by the library. +constexpr engine_schema latest_schema = engine_schema::schema_2_21_2; + +/// The most recent legacy schema version supported by the library. +constexpr engine_schema latest_v1_schema = engine_schema::schema_1_18_0_os; + +/// The most recent "Database2" schema version supported by the library. +constexpr engine_schema latest_v2_schema = engine_schema::schema_2_21_2; + +/// Get a string representation of the schema. +inline std::string to_string(const engine_schema& v) +{ + switch (v) + { + case engine_schema::schema_1_6_0: return "1.6.0"; + case engine_schema::schema_1_7_1: return "1.7.1"; + case engine_schema::schema_1_9_1: return "1.9.1"; + case engine_schema::schema_1_11_1: return "1.11.1"; + case engine_schema::schema_1_13_0: return "1.13.0"; + case engine_schema::schema_1_13_1: return "1.13.1"; + case engine_schema::schema_1_13_2: return "1.13.2"; + case engine_schema::schema_1_15_0: return "1.15.0"; + case engine_schema::schema_1_17_0: return "1.17.0"; + case engine_schema::schema_1_18_0_desktop: return "1.18.0 (Desktop)"; + case engine_schema::schema_1_18_0_os: return "1.18.0 (OS)"; + case engine_schema::schema_2_18_0: return "2.18.0"; + case engine_schema::schema_2_20_1: return "2.20.1"; + case engine_schema::schema_2_20_2: return "2.20.2"; + case engine_schema::schema_2_20_3: return "2.20.3"; + case engine_schema::schema_2_21_0: return "2.21.0"; + case engine_schema::schema_2_21_1: return "2.21.1"; + case engine_schema::schema_2_21_2: return "2.21.2"; + case engine_schema::schema_3_0_0: return "3.0.0"; + } + + return "(unknown schema with ordinal " + + std::to_string(static_cast(v)) + ")"; +} + +/// Get a human-readable string informing of the Engine OS/DJ versions that +/// this schema can be used with, e.g. "Engine DJ/OS 3.1.0 to 3.4.0". +inline std::string to_application_version_string(const engine_schema& v) +{ + switch (v) + { + case engine_schema::schema_1_6_0: return "Engine DJ OS 1.0.0"; + case engine_schema::schema_1_7_1: return "Engine DJ OS 1.0.3"; + case engine_schema::schema_1_9_1: return "Engine Prime 1.1.1"; + case engine_schema::schema_1_11_1: return "Engine DJ OS 1.2.0"; + case engine_schema::schema_1_13_0: return "Engine DJ OS 1.2.2"; + case engine_schema::schema_1_13_1: return "Engine Prime 1.2.2"; + case engine_schema::schema_1_13_2: return "Engine DJ OS 1.3.1"; + case engine_schema::schema_1_15_0: return "Engine DJ OS 1.4.0"; + case engine_schema::schema_1_17_0: return "Engine DJ OS 1.5.1/1.5.2"; + case engine_schema::schema_1_18_0_desktop: + return "Engine Prime 1.5.1/1.6.0/1.6.1"; + case engine_schema::schema_1_18_0_os: + return "Engine DJ OS 1.6.0/1.6.1/1.6.2"; + case engine_schema::schema_2_18_0: + return "Engine DJ Desktop/OS 2.0.x to 2.1.x"; + case engine_schema::schema_2_20_1: + return "Engine DJ Desktop/OS 2.2.x to 2.3.x"; + case engine_schema::schema_2_20_2: return "Engine DJ Desktop/OS 2.4.0"; + case engine_schema::schema_2_20_3: + return "Engine DJ Desktop/OS 3.0.0 to 3.0.1"; + case engine_schema::schema_2_21_0: + return "Engine DJ Desktop/OS 3.1.0 to 3.4.0"; + case engine_schema::schema_2_21_1: return "Engine DJ Desktop/OS 4.0.0"; + case engine_schema::schema_2_21_2: return "Engine DJ Desktop/OS 4.0.1"; + case engine_schema::schema_3_0_0: return "Engine DJ Desktop/OS 4.1.0"; + } + + return "Engine versions unknown (" + to_string(v) + ")"; +} + +inline std::ostream& operator<<(std::ostream& os, const engine_schema& v) +{ + return os << to_string(v); +} + +} // namespace djinterop::engine + +#endif // DJINTEROP_ENGINE_ENGINE_SCHEMA_HPP diff --git a/include/djinterop/engine/engine_version.hpp b/include/djinterop/engine/engine_version.hpp deleted file mode 100644 index e543428..0000000 --- a/include/djinterop/engine/engine_version.hpp +++ /dev/null @@ -1,114 +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 . - */ - -#pragma once -#ifndef DJINTEROP_ENGINE_ENGINE_VERSION_HPP -#define DJINTEROP_ENGINE_ENGINE_VERSION_HPP - -#if __cplusplus < 201703L -#error This library needs at least a C++17 compliant compiler -#endif - -#include - -#include -#include - -namespace djinterop::engine -{ -/// Enumeration of types of Engine database. -enum class engine_database_type -{ - /// Engine database for a desktop application, such as Engine Prime. - desktop, - - /// Engine database for a hardware device, such as DJ players. - os, -}; - -/// The `engine_version` struct represents a way to identify the version -/// of an Engine database. -struct DJINTEROP_PUBLIC engine_version -{ - /// Headline version. - semantic_version version; - - /// Engine database type (Desktop or OS). - engine_database_type database_type; - - /// Descriptive version name. - const char* name; - - /// Database schema version. - semantic_version schema_version; - - /// Test whether the version has a version 2 database schema. - /// - /// \return Returns `true` if the version has v2 schema, or `false` if not. - [[nodiscard]] bool is_v2_schema() const noexcept - { - return schema_version.maj == 2; - } -}; - -inline std::ostream& operator<<(std::ostream& os, const engine_version& v) -{ - os << v.name; - return os; -} - -inline bool operator==(const engine_version& a, const engine_version& b) -{ - return a.version == b.version && a.database_type == b.database_type; -} - -inline bool operator!=(const engine_version& a, const engine_version& b) -{ - return !(a == b); -} - -inline bool operator>=(const engine_version& a, const engine_version& b) -{ - if (a.version == b.version) - return a.database_type >= b.database_type; - return a.version >= b.version; -} - -inline bool operator>(const engine_version& a, const engine_version& b) -{ - if (a.version == b.version) - return a.database_type > b.database_type; - return a.version > b.version; -} - -inline bool operator<=(const engine_version& a, const engine_version& b) -{ - if (a.version == b.version) - return a.database_type <= b.database_type; - return a.version <= b.version; -} - -inline bool operator<(const engine_version& a, const engine_version& b) -{ - if (a.version == b.version) - return a.database_type < b.database_type; - return a.version < b.version; -} - -} // namespace djinterop::engine - -#endif // DJINTEROP_ENGINE_ENGINE_VERSION_HPP diff --git a/include/djinterop/engine/v2/engine_library.hpp b/include/djinterop/engine/v2/engine_library.hpp index 38a7d5d..285e70f 100644 --- a/include/djinterop/engine/v2/engine_library.hpp +++ b/include/djinterop/engine/v2/engine_library.hpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include #include @@ -59,19 +59,19 @@ class DJINTEROP_PUBLIC engine_library /// Make a new, empty library of a given version. /// /// \param directory Directory in which to create the new library. - /// \param version Version to create. + /// \param schema Version to create. /// \return Returns the new Engine library. static engine_library create( - const std::string& directory, const engine_version& version); + const std::string& directory, const engine_schema& schema); /// Make a new, empty, in-memory library of a given version. /// /// Any changes made to the library will not persist beyond destruction /// of the class instance. /// - /// \param version Version to create. + /// \param schema Version to create. /// \return Returns the new temporary Engine library. - static engine_library create_temporary(const engine_version& version); + static engine_library create_temporary(const engine_schema& schema); /// Test whether an Engine Library already exists in the given directory. /// @@ -91,10 +91,10 @@ class DJINTEROP_PUBLIC engine_library /// \return Returns the top-level directory. [[nodiscard]] std::string directory() const; - /// Get the version of the Engine library. + /// Get the schema version of the Engine library. /// - /// \return Returns the Engine version. - [[nodiscard]] engine_version version() const; + /// \return Returns the Engine schema version. + [[nodiscard]] engine_schema schema() const; /// Get a class representing the `ChangeLog` table. [[nodiscard]] change_log_table change_log() const diff --git a/src/djinterop/engine/engine.cpp b/src/djinterop/engine/engine.cpp index 1e0f384..bad500f 100644 --- a/src/djinterop/engine/engine.cpp +++ b/src/djinterop/engine/engine.cpp @@ -25,7 +25,6 @@ #include #include "../util/filesystem.hpp" -#include "schema/schema.hpp" #include "track_utils.hpp" #include "v1/engine_database_impl.hpp" @@ -33,28 +32,7 @@ namespace djinterop::engine { namespace { -inline std::optional get_column_type( - sqlite::database& db, const std::string& table_name, - const std::string& column_name) -{ - std::optional column_type; - - db << "PRAGMA table_info('" + table_name + "')" >> - [&]([[maybe_unused]] int col_id, const std::string& col_name, - const std::string& col_type, [[maybe_unused]] int nullable, - [[maybe_unused]] const std::string& default_value, - [[maybe_unused]] int part_of_pk) - { - if (col_name == column_name) - { - column_type = col_type; - } - }; - - return column_type; -} - -engine_version detect_version(const std::string& directory) +bool detect_is_database2(const std::string& directory) { if (!djinterop::util::path_exists(directory)) { @@ -68,53 +46,20 @@ engine_version detect_version(const std::string& directory) auto v2_m_db_path = directory + "/Database2/m.db"; auto v1_m_db_path_exists = djinterop::util::path_exists(v1_m_db_path); auto v2_m_db_path_exists = djinterop::util::path_exists(v2_m_db_path); - auto m_db_path = v1_m_db_path_exists ? v1_m_db_path : v2_m_db_path; + if (!v1_m_db_path_exists && !v2_m_db_path_exists) { throw database_not_found{"Neither m.db nor Database2/m.db was found"}; } - sqlite::database db{m_db_path}; - - // Check that the `Information` table has been created. - std::string sql = - "SELECT COUNT(*) AS rows " - "FROM sqlite_master " - "WHERE name = 'Information'"; - int32_t table_count; - db << sql >> table_count; - if (table_count != 1) + if (v1_m_db_path_exists && v2_m_db_path_exists) { - throw database_inconsistency{ - "Did not find an `Information` table in the database"}; + throw database_not_found{ + "Found m.db and Database2/m.db files, which is not supposed to " + "happen"}; } - semantic_version schema_version; - db << "SELECT schemaVersionMajor, schemaVersionMinor, " - "schemaVersionPatch FROM Information" >> - std::tie(schema_version.maj, schema_version.min, schema_version.pat); - - // Some schema versions have different variants, meaning that the version - // number alone is insufficient. Detect the variant where required. - if (schema_version.maj == 1 && schema_version.min == 18 && - schema_version.pat == 0) - { - auto has_numeric_bools = - get_column_type(db, "Track", "isExternalTrack") == - std::string{"NUMERIC"}; - return has_numeric_bools ? djinterop::engine::desktop_1_5_1 - : djinterop::engine::os_1_6_0; - } - - for (auto&& candidate_version : all_versions) - { - if (schema_version == candidate_version.schema_version) - { - return candidate_version; - } - } - - throw unsupported_engine_database{schema_version}; + return v2_m_db_path_exists; } inline void hydrate_database( @@ -132,33 +77,33 @@ inline void hydrate_database( } // anonymous namespace database create_database( - const std::string& directory, const engine_version& version) + const std::string& directory, const engine_schema& schema) { - if (version.version.maj >= 2) + if (schema >= engine_schema::schema_2_18_0) { - auto library = v2::engine_library::create(directory, version); + auto library = v2::engine_library::create(directory, schema); return library.database(); } - auto storage = v1::engine_storage::create(directory, version); + auto storage = v1::engine_storage::create(directory, schema); return database{std::make_shared(storage)}; } -database create_temporary_database(const engine_version& version) +database create_temporary_database(const engine_schema& schema) { - if (version.version.maj >= 2) + if (schema >= engine_schema::schema_2_18_0) { - auto library = v2::engine_library::create_temporary(version); + auto library = v2::engine_library::create_temporary(schema); return library.database(); } - auto storage = v1::engine_storage::create_temporary(version); + auto storage = v1::engine_storage::create_temporary(schema); return database{std::make_shared(storage)}; } database create_database_from_scripts( const std::string& db_directory, const std::string& script_directory, - engine_version& loaded_version) + engine_schema& loaded_schema) { if (!djinterop::util::path_exists(db_directory)) { @@ -197,22 +142,22 @@ database create_database_from_scripts( hydrate_database(v2_m_db_path, v2_m_db_sql_path); } - return load_database(db_directory, loaded_version); + return load_database(db_directory, loaded_schema); } database create_or_load_database( - const std::string& directory, const engine_version& version, bool& created, - engine_version& loaded_version) + const std::string& directory, const engine_schema& schema, bool& created, + engine_schema& loaded_schema) { try { created = false; - return load_database(directory, loaded_version); + return load_database(directory, loaded_schema); } catch ([[maybe_unused]] database_not_found& e) { created = true; - return create_database(directory, version); + return create_database(directory, schema); } } @@ -231,18 +176,19 @@ bool database_exists(const std::string& directory) } database load_database( - const std::string& directory, engine_version& loaded_version) + const std::string& directory, engine_schema& loaded_schema) { - loaded_version = detect_version(directory); - if (loaded_version.version.maj >= 2) + const auto is_database2 = detect_is_database2(directory); + + if (is_database2) { - // TODO (mr-smidge): inefficient as ctor reads the version again. auto library = v2::engine_library{directory}; + loaded_schema = library.schema(); return library.database(); } - auto storage = - std::make_shared(directory, loaded_version); + auto storage = std::make_shared(directory); + loaded_schema = storage->schema; return database{std::make_shared(storage)}; } @@ -267,8 +213,7 @@ std::vector normalize_beatgrid( { auto after_first_marker_iter = std::find_if( - beatgrid.begin(), beatgrid.end(), - [](const beatgrid_marker& marker) + beatgrid.begin(), beatgrid.end(), [](const beatgrid_marker& marker) { return marker.sample_offset > 0; }); if (after_first_marker_iter != beatgrid.begin()) { diff --git a/src/djinterop/engine/schema/schema.cpp b/src/djinterop/engine/schema/schema.cpp index 24acf7a..1a42cf1 100644 --- a/src/djinterop/engine/schema/schema.cpp +++ b/src/djinterop/engine/schema/schema.cpp @@ -15,7 +15,10 @@ along with libdjinterop. If not, see . */ -#include +#include +#include + +#include #include "schema.hpp" #include "schema_1_11_1.hpp" @@ -36,64 +39,209 @@ #include "schema_2_21_0.hpp" #include "schema_2_21_1.hpp" #include "schema_2_21_2.hpp" +#include "schema_3_0_0.hpp" namespace djinterop::engine::schema { +namespace +{ +std::optional get_column_type( + sqlite::database& db, const std::string& table_name, + const std::string& column_name) +{ + std::optional column_type; + + db << "PRAGMA table_info('" + table_name + "')" >> + [&]([[maybe_unused]] int col_id, const std::string& col_name, + const std::string& col_type, [[maybe_unused]] int nullable, + [[maybe_unused]] const std::string& default_value, + [[maybe_unused]] int part_of_pk) + { + if (col_name == column_name) + { + column_type = col_type; + } + }; + + return column_type; +} +} // namespace + std::unique_ptr make_schema_creator_validator( - const engine_version& version) + const engine_schema& schema) +{ + switch (schema) + { + case engine_schema::schema_1_6_0: + return std::make_unique(); + case engine_schema::schema_1_7_1: + return std::make_unique(); + case engine_schema::schema_1_9_1: + return std::make_unique(); + case engine_schema::schema_1_11_1: + return std::make_unique(); + case engine_schema::schema_1_13_0: + return std::make_unique(); + case engine_schema::schema_1_13_1: + return std::make_unique(); + case engine_schema::schema_1_13_2: + return std::make_unique(); + case engine_schema::schema_1_15_0: + return std::make_unique(); + case engine_schema::schema_1_17_0: + return std::make_unique(); + case engine_schema::schema_1_18_0_desktop: + return std::make_unique(); + case engine_schema::schema_1_18_0_os: + return std::make_unique(); + case engine_schema::schema_2_18_0: + return std::make_unique(); + case engine_schema::schema_2_20_1: + return std::make_unique(); + case engine_schema::schema_2_20_2: + return std::make_unique(); + case engine_schema::schema_2_20_3: + return std::make_unique(); + case engine_schema::schema_2_21_0: + return std::make_unique(); + case engine_schema::schema_2_21_1: + return std::make_unique(); + case engine_schema::schema_2_21_2: + return std::make_unique(); + case engine_schema::schema_3_0_0: + return std::make_unique(); + } + + throw unsupported_operation{ + "Engine schema not implemented for version " + to_string(schema)}; +} + +engine_schema detect_schema( + sqlite::database& db, const std::string& db_schema_name) { - if (version == os_1_0_0) - return std::make_unique(); - else if (version == os_1_0_3) - return std::make_unique(); - else if (version == desktop_1_1_1) - return std::make_unique(); - else if (version == os_1_2_0) - return std::make_unique(); - else if (version == os_1_2_2) - return std::make_unique(); - else if (version == desktop_1_2_2) - return std::make_unique(); - else if (version == os_1_3_1) - return std::make_unique(); - else if (version == os_1_4_0) - return std::make_unique(); - else if (version == os_1_5_1) - return std::make_unique(); - else if (version == desktop_1_5_1) - return std::make_unique(); - else if (version == os_1_6_0) - return std::make_unique(); - else if (version == os_2_0_0) - return std::make_unique(); - else if (version == desktop_2_0_0) - return std::make_unique(); - else if (version == desktop_2_2_0) - return std::make_unique(); - else if (version == os_2_2_0) - return std::make_unique(); - else if (version == desktop_2_4_0) - return std::make_unique(); - else if (version == os_2_4_0) - return std::make_unique(); - else if (version == desktop_3_0_0) - return std::make_unique(); - else if (version == os_3_0_0) - return std::make_unique(); - else if (version == desktop_3_1_0) - return std::make_unique(); - else if (version == os_3_1_0) - return std::make_unique(); - else if (version == desktop_4_0_0) - return std::make_unique(); - else if (version == os_4_0_0) - return std::make_unique(); - else if (version == desktop_4_0_1) - return std::make_unique(); - else if (version == os_4_0_1) - return std::make_unique(); - - throw unsupported_engine_database{version.schema_version}; + const auto master_table = db_schema_name.empty() + ? "sqlite_master" + : db_schema_name + ".sqlite_master"; + const auto information_table = db_schema_name.empty() + ? "Information" + : db_schema_name + ".Information"; + + // Check that the `Information` table has been created. + std::string sql = "SELECT COUNT(*) AS rows FROM " + master_table + + " WHERE name = 'Information'"; + int32_t table_count; + db << sql >> table_count; + + if (table_count != 1) + { + throw database_inconsistency{ + "Did not find an `" + information_table + + "` table in the music database"}; + } + + semantic_version version{}; + db << "SELECT schemaVersionMajor, schemaVersionMinor, " + "schemaVersionPatch FROM " + + information_table >> + std::tie(version.maj, version.min, version.pat); + + auto make_err_message = [](const semantic_version& v) + { return "Unsupported schema: " + to_string(v); }; + +#define REQUIRE_PATCH_VERSION(v, expected) \ + if (v.pat != expected) \ + { \ + throw unsupported_database{make_err_message(v)}; \ + } + + switch (version.maj) + { + case 1: + switch (version.min) + { + case 6: + REQUIRE_PATCH_VERSION(version, 0); + return engine_schema::schema_1_6_0; + case 7: + REQUIRE_PATCH_VERSION(version, 1); + return engine_schema::schema_1_7_1; + case 9: + REQUIRE_PATCH_VERSION(version, 1); + return engine_schema::schema_1_9_1; + case 11: + REQUIRE_PATCH_VERSION(version, 1); + return engine_schema::schema_1_11_1; + case 13: + switch (version.pat) + { + case 0: return engine_schema::schema_1_13_0; + case 1: return engine_schema::schema_1_13_1; + case 2: return engine_schema::schema_1_13_2; + default: + throw unsupported_database{ + make_err_message(version)}; + } + case 15: + REQUIRE_PATCH_VERSION(version, 0); + return engine_schema::schema_1_15_0; + case 17: + REQUIRE_PATCH_VERSION(version, 0); + return engine_schema::schema_1_17_0; + case 18: + { + REQUIRE_PATCH_VERSION(version, 0); + // Schema version 1.18.0 has different variants, meaning + // that the version number alone is insufficient. Detect + // the variant by looking at other parts of the schema. + const auto has_numeric_bools = + get_column_type(db, "Track", "isExternalTrack") == + std::string{"NUMERIC"}; + return has_numeric_bools + ? engine_schema::schema_1_18_0_desktop + : engine_schema::schema_1_18_0_os; + } + default: throw unsupported_database{make_err_message(version)}; + } + case 2: + switch (version.min) + { + case 18: + REQUIRE_PATCH_VERSION(version, 0); + return engine_schema::schema_2_18_0; + case 20: + switch (version.pat) + { + case 1: return engine_schema::schema_2_20_1; + case 2: return engine_schema::schema_2_20_2; + case 3: return engine_schema::schema_2_20_3; + default: + throw unsupported_database{ + make_err_message(version)}; + } + case 21: + switch (version.pat) + { + case 0: return engine_schema::schema_2_21_0; + case 1: return engine_schema::schema_2_21_1; + case 2: return engine_schema::schema_2_21_2; + default: + throw unsupported_database{ + make_err_message(version)}; + } + default: throw unsupported_database{make_err_message(version)}; + } + case 3: + switch (version.min) + { + case 0: + REQUIRE_PATCH_VERSION(version, 0); + return engine_schema::schema_3_0_0; + default: throw unsupported_database{make_err_message(version)}; + } + default: throw unsupported_database{make_err_message(version)}; + } + +#undef REQUIRE_PATCH_VERSION } } // namespace djinterop::engine::schema diff --git a/src/djinterop/engine/schema/schema.hpp b/src/djinterop/engine/schema/schema.hpp index 8054705..70b34cb 100644 --- a/src/djinterop/engine/schema/schema.hpp +++ b/src/djinterop/engine/schema/schema.hpp @@ -17,13 +17,11 @@ #pragma once -#include -#include +#include #include -#include -#include +#include namespace djinterop::engine::schema { @@ -67,6 +65,9 @@ class schema_v2 : public schema_creator_validator }; std::unique_ptr make_schema_creator_validator( - const engine_version& version); + const engine_schema& schema); + +engine_schema detect_schema( + sqlite::database& db, const std::string& db_schema_name = ""); } // namespace djinterop::engine::schema diff --git a/src/djinterop/engine/schema/schema_3_0_0.cpp b/src/djinterop/engine/schema/schema_3_0_0.cpp new file mode 100644 index 0000000..fe56c1d --- /dev/null +++ b/src/djinterop/engine/schema/schema_3_0_0.cpp @@ -0,0 +1,551 @@ +/* + 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 . + */ +#include + +#include + +#include "../../util/random.hpp" +#include "schema_3_0_0.hpp" +#include "schema_validate_utils.hpp" + +namespace djinterop::engine::schema +{ +void schema_3_0_0::verify_master_list(sqlite::database& db) const +{ + // Schema 3.0.0 reinstates `PerformanceData` as a separate table. + { + master_list items{db, "table"}; + auto iter = items.begin(), end = items.end(); + validate(iter, end, "table", "AlbumArt", "AlbumArt"); + ++iter; + validate(iter, end, "table", "Information", "Information"); + ++iter; + validate(iter, end, "table", "Pack", "Pack"); + ++iter; + validate(iter, end, "table", "PerformanceData", "PerformanceData"); + ++iter; + validate(iter, end, "table", "Playlist", "Playlist"); + ++iter; + validate(iter, end, "table", "PlaylistEntity", "PlaylistEntity"); + ++iter; + validate(iter, end, "table", "PreparelistEntity", "PreparelistEntity"); + ++iter; + validate(iter, end, "table", "Smartlist", "Smartlist"); + ++iter; + validate(iter, end, "table", "Track", "Track"); + ++iter; + validate(iter, end, "table", "sqlite_sequence", "sqlite_sequence"); + ++iter; + validate_no_more(iter, end); + } + { + master_list items{db, "view"}; + auto iter = items.begin(), end = items.end(); + validate(iter, end, "view", "ChangeLog", "ChangeLog"); + ++iter; + validate( + iter, end, "view", "PlaylistAllChildren", "PlaylistAllChildren"); + ++iter; + validate(iter, end, "view", "PlaylistAllParent", "PlaylistAllParent"); + ++iter; + validate(iter, end, "view", "PlaylistPath", "PlaylistPath"); + ++iter; + validate_no_more(iter, end); + } +} + +void schema_3_0_0::verify_performance_data(sqlite::database& db) const +{ + { + table_info cols{db, "PerformanceData"}; + auto iter = cols.begin(), end = cols.end(); + validate(iter, end, "activeOnLoadLoops", "INTEGER", 0, "", 0); + ++iter; + validate(iter, end, "beatData", "BLOB", 0, "", 0); + ++iter; + validate(iter, end, "loops", "BLOB", 0, "", 0); + ++iter; + validate(iter, end, "overviewWaveFormData", "BLOB", 0, "", 0); + ++iter; + validate(iter, end, "quickCues", "BLOB", 0, "", 0); + ++iter; + validate(iter, end, "thirdPartySourceId", "INTEGER", 0, "", 0); + ++iter; + validate(iter, end, "trackData", "BLOB", 0, "", 0); + ++iter; + validate(iter, end, "trackId", "INTEGER", 0, "", 1); + ++iter; + validate_no_more(iter, end); + } + { + index_list indices{db, "PerformanceData"}; + auto iter = indices.begin(), end = indices.end(); + validate_no_more(iter, end); + } +} + +void schema_3_0_0::verify_track(sqlite::database& db) const +{ + // Schema 3.0.0 moves some columns from Track to PerformanceData. + { + table_info cols{db, "Track"}; + auto iter = cols.begin(), end = cols.end(); + validate(iter, end, "album", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "albumArt", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "albumArtId", "INTEGER", 0, "", 0); + ++iter; + validate(iter, end, "artist", "TEXT", 0, "", 0); + ++iter; + 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, "comment", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "composer", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "dateAdded", "DATETIME", 0, "", 0); + ++iter; + validate(iter, end, "dateCreated", "DATETIME", 0, "", 0); + ++iter; + validate(iter, end, "explicitLyrics", "BOOLEAN", 0, "", 0); + ++iter; + validate(iter, end, "fileBytes", "INTEGER", 0, "", 0); + ++iter; + validate(iter, end, "fileType", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "filename", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "genre", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "id", "INTEGER", 0, "", 1); + ++iter; + validate(iter, end, "isAnalyzed", "BOOLEAN", 0, "", 0); + ++iter; + validate(iter, end, "isAvailable", "BOOLEAN", 0, "", 0); + ++iter; + validate(iter, end, "isBeatGridLocked", "BOOLEAN", 0, "", 0); + ++iter; + validate(iter, end, "isMetadataImported", "BOOLEAN", 0, "", 0); + ++iter; + validate( + iter, end, "isMetadataOfPackedTrackChanged", "BOOLEAN", 0, "", 0); + ++iter; + validate( + iter, end, "isPerfomanceDataOfPackedTrackChanged", "BOOLEAN", 0, "", + 0); + ++iter; + validate(iter, end, "isPlayed", "BOOLEAN", 0, "", 0); + ++iter; + validate(iter, end, "key", "INTEGER", 0, "", 0); + ++iter; + validate(iter, end, "label", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "lastEditTime", "DATETIME", 0, "", 0); + ++iter; + validate(iter, end, "length", "INTEGER", 0, "", 0); + ++iter; + validate(iter, end, "originDatabaseUuid", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "originTrackId", "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, "playedIndicator", "INTEGER", 0, "", 0); + ++iter; + validate(iter, end, "rating", "INTEGER", 0, "", 0); + ++iter; + validate(iter, end, "remixer", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "streamingFlags", "INTEGER", 0, "", 0); + ++iter; + validate(iter, end, "streamingSource", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "timeLastPlayed", "DATETIME", 0, "", 0); + ++iter; + validate(iter, end, "title", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "uri", "TEXT", 0, "", 0); + ++iter; + validate(iter, end, "year", "INTEGER", 0, "", 0); + ++iter; + validate_no_more(iter, end); + } + { + index_list indices{db, "Track"}; + auto iter = indices.begin(), end = indices.end(); + validate(iter, end, "index_Track_album", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_albumArtId", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_artist", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_bpmAnalyzed", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_dateAdded", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_filename", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_genre", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_key", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_length", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_rating", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_title", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_uri", 0, "c", 0); + ++iter; + validate(iter, end, "index_Track_year", 0, "c", 0); + ++iter; + validate(iter, end, "sqlite_autoindex_Track_1", 1, "u", 0); + ++iter; + validate(iter, end, "sqlite_autoindex_Track_2", 1, "u", 0); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_album"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "album"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_albumArtId"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "albumArtId"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_artist"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "artist"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_bpmAnalyzed"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, ""); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_dateAdded"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "dateAdded"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_filename"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "filename"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_genre"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "genre"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_key"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "key"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_length"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "length"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_rating"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "rating"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_title"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "title"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_uri"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "uri"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "index_Track_year"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "year"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "sqlite_autoindex_Track_1"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "originDatabaseUuid"); + ++iter; + validate(iter, end, 1, "originTrackId"); + ++iter; + validate_no_more(iter, end); + } + { + index_info ii{db, "sqlite_autoindex_Track_2"}; + auto iter = ii.begin(), end = ii.end(); + validate(iter, end, 0, "path"); + ++iter; + validate_no_more(iter, end); + } +} + +void schema_3_0_0::verify(sqlite::database& db) const +{ + verify_master_list(db); + + verify_information(db); + verify_album_art(db); + verify_pack(db); + verify_performance_data(db); + verify_playlist(db); + verify_playlist_entity(db); + verify_preparelist_entity(db); + verify_smartlist(db); + verify_track(db); +} + +void schema_3_0_0::create(sqlite::database& db) +{ + db << "CREATE TABLE Information ( id INTEGER PRIMARY KEY AUTOINCREMENT, " + " uuid TEXT, schemaVersionMajor INTEGER, schemaVersionMinor " + "INTEGER, schemaVersionPatch INTEGER, " + "currentPlayedIndiciator INTEGER, " + "lastRekordBoxLibraryImportReadCounter INTEGER);"; + db << "INSERT INTO Information " + "VALUES(1,'43b432fc-8c09-40cd-8ef4-9fcb9a853f84',3,0,0," + "8054931482920806910,NULL);"; + db << "CREATE TABLE AlbumArt ( id INTEGER PRIMARY KEY AUTOINCREMENT, " + "hash TEXT, albumArt BLOB );"; + db << "CREATE TABLE Pack ( id INTEGER PRIMARY KEY AUTOINCREMENT, packId " + "TEXT, changeLogDatabaseUuid TEXT, changeLogId INTEGER, " + "lastPackTime DATETIME );"; + db << "CREATE TABLE Playlist ( id INTEGER PRIMARY KEY AUTOINCREMENT, " + "title TEXT, parentListId INTEGER, isPersisted BOOLEAN, " + "nextListId INTEGER, lastEditTime DATETIME, isExplicitlyExported " + "BOOLEAN, CONSTRAINT C_NAME_UNIQUE_FOR_PARENT UNIQUE (title, " + "parentListId), CONSTRAINT C_NEXT_LIST_ID_UNIQUE_FOR_PARENT UNIQUE " + "(parentListId, nextListId) );"; + db << "CREATE TABLE PlaylistEntity ( id INTEGER PRIMARY KEY " + "AUTOINCREMENT, listId INTEGER, trackId INTEGER, " + "databaseUuid TEXT, nextEntityId INTEGER, membershipReference " + "INTEGER, CONSTRAINT C_NAME_UNIQUE_FOR_LIST UNIQUE (listId, " + "databaseUuid, trackId), FOREIGN KEY (listId) REFERENCES Playlist " + "(id) ON DELETE CASCADE );"; + db << "CREATE TABLE Smartlist ( listUuid TEXT NOT NULL PRIMARY KEY, " + " title TEXT, parentPlaylistPath TEXT, nextPlaylistPath TEXT, " + " nextListUuid TEXT, rules TEXT, lastEditTime DATETIME, " + "CONSTRAINT C_NAME_UNIQUE_FOR_PARENT UNIQUE (title, " + "parentPlaylistPath), CONSTRAINT C_NEXT_LIST_UNIQUE_FOR_PARENT " + "UNIQUE (parentPlaylistPath, nextPlaylistPath, nextListUuid) );"; + db << "CREATE TABLE Track ( id INTEGER PRIMARY KEY AUTOINCREMENT, " + "playOrder INTEGER, length INTEGER, bpm INTEGER, year " + "INTEGER, path TEXT, filename TEXT, bitrate INTEGER, " + "bpmAnalyzed REAL, albumArtId INTEGER, fileBytes INTEGER, " + "title TEXT, artist TEXT, album TEXT, genre TEXT, " + "comment TEXT, label TEXT, composer TEXT, remixer TEXT, " + "key INTEGER, rating INTEGER, albumArt TEXT, timeLastPlayed " + "DATETIME, isPlayed BOOLEAN, fileType TEXT, isAnalyzed " + "BOOLEAN, dateCreated DATETIME, dateAdded DATETIME, " + "isAvailable BOOLEAN, isMetadataOfPackedTrackChanged BOOLEAN, " + " isPerfomanceDataOfPackedTrackChanged BOOLEAN, playedIndicator " + "INTEGER, isMetadataImported BOOLEAN, pdbImportKey INTEGER, " + " streamingSource TEXT, uri TEXT, isBeatGridLocked BOOLEAN, " + "originDatabaseUuid TEXT, originTrackId INTEGER, streamingFlags " + "INTEGER, explicitLyrics BOOLEAN, lastEditTime DATETIME, " + "CONSTRAINT C_originDatabaseUuid_originTrackId UNIQUE " + "(originDatabaseUuid, originTrackId), CONSTRAINT C_path UNIQUE " + "(path), FOREIGN KEY (albumArtId) REFERENCES AlbumArt (id) ON " + "DELETE RESTRICT );"; + db << "CREATE TABLE PerformanceData ( trackId INTEGER PRIMARY KEY, " + "trackData BLOB, overviewWaveFormData BLOB, beatData BLOB, " + "quickCues BLOB, loops BLOB, thirdPartySourceId INTEGER, " + "activeOnLoadLoops INTEGER, FOREIGN KEY(trackId) REFERENCES " + "Track(id) ON DELETE CASCADE ON UPDATE CASCADE );"; + db << "CREATE TABLE PreparelistEntity ( id INTEGER PRIMARY KEY " + "AUTOINCREMENT, trackId INTEGER, trackNumber INTEGER, " + "FOREIGN KEY (trackId) REFERENCES Track (id) ON DELETE CASCADE );"; + db << "DELETE FROM sqlite_sequence;"; + db << "INSERT INTO sqlite_sequence VALUES('Information',1);"; + db << "CREATE INDEX index_AlbumArt_hash ON AlbumArt (hash);"; + db << "CREATE INDEX index_PlaylistEntity_nextEntityId_listId ON " + "PlaylistEntity(nextEntityId, listId);"; + db << "CREATE TRIGGER trigger_after_insert_Pack_timestamp AFTER INSERT ON " + "Pack FOR EACH ROW WHEN NEW.lastPackTime IS NULL BEGIN UPDATE " + "Pack SET lastPackTime = strftime('%s') WHERE ROWID = NEW.ROWID; " + "END;"; + db << "CREATE TRIGGER trigger_after_insert_Pack_changeLogId AFTER INSERT " + "ON Pack FOR EACH ROW WHEN NEW.changeLogId = 0 BEGIN UPDATE Pack " + "SET changeLogId = 1 WHERE ROWID = NEW.ROWID; END;"; + db << "CREATE VIEW ChangeLog (id, trackId) AS SELECT 0, 0 WHERE FALSE;"; + db << "CREATE TRIGGER trigger_before_insert_List BEFORE INSERT ON Playlist " + "FOR EACH ROW BEGIN UPDATE Playlist SET nextListId = -(1 + " + "nextListId) WHERE nextListId = NEW.nextListId AND parentListId = " + "NEW.parentListId; END;"; + db << "CREATE TRIGGER trigger_after_insert_List AFTER INSERT ON Playlist " + "FOR EACH ROW BEGIN UPDATE Playlist SET nextListId = " + "NEW.id WHERE nextListId = -(1 + NEW.nextListId) AND " + "parentListId = NEW.parentListId; END;"; + db << "CREATE TRIGGER trigger_after_delete_List AFTER DELETE ON Playlist " + "FOR EACH ROW BEGIN UPDATE Playlist SET nextListId = " + "OLD.nextListId WHERE nextListId = OLD.id; DELETE FROM Playlist " + " WHERE parentListId = OLD.id; END;"; + db << "CREATE TRIGGER trigger_after_update_isPersistParent AFTER UPDATE ON " + "Playlist WHEN (old.isPersisted = 0 AND new.isPersisted = 1) " + " OR (old.parentListId != new.parentListId AND new.isPersisted = " + "1) BEGIN UPDATE Playlist SET isPersisted = 1 WHERE " + "id IN (SELECT parentListId FROM PlaylistAllParent WHERE id=new.id); " + "END;"; + db << "CREATE TRIGGER trigger_after_update_isPersistChild AFTER UPDATE ON " + "Playlist WHEN old.isPersisted = 1 AND new.isPersisted = 0 " + "BEGIN UPDATE Playlist SET isPersisted = 0 WHERE id " + "IN (SELECT childListId FROM PlaylistAllChildren WHERE id=new.id); " + "END;"; + db << "CREATE TRIGGER trigger_after_insert_isPersist AFTER INSERT ON " + "Playlist WHEN new.isPersisted = 1 BEGIN UPDATE Playlist SET " + " isPersisted = 1 WHERE id IN (SELECT parentListId FROM " + "PlaylistAllParent WHERE id=new.id); END;"; + db << "CREATE VIEW PlaylistAllParent AS WITH FindAllParent AS ( SELECT " + "id, parentListId FROM Playlist UNION ALL SELECT " + "recursiveCTE.id, Plist.parentListId FROM Playlist Plist INNER JOIN " + "FindAllParent recursiveCTE ON recursiveCTE.parentListId = " + "Plist.id ) SELECT * FROM FindAllParent;"; + db << "CREATE VIEW PlaylistAllChildren AS WITH FindAllChild AS ( SELECT " + "id, id as childListId FROM Playlist UNION ALL SELECT " + "recursiveCTE.id, Plist.id FROM Playlist Plist INNER JOIN " + "FindAllChild recursiveCTE ON recursiveCTE.childListId = " + "Plist.parentListId ) SELECT * FROM FindAllChild WHERE id <> " + "childListId;"; + db << "CREATE VIEW PlaylistPath AS WITH RECURSIVE Heirarchy AS ( SELECT " + "id AS child, parentListId AS parent, title AS name, 1 AS depth FROM " + "Playlist UNION ALL SELECT child, parentListId AS parent, " + "title AS name, h.depth + 1 AS depth FROM Playlist c JOIN Heirarchy " + "h ON h.parent = c.id ORDER BY depth DESC ), OrderedList AS ( " + " SELECT id , nextListId, 1 AS position FROM Playlist WHERE " + "nextListId = 0 UNION ALL SELECT c.id , c.nextListId , " + "l.position + 1 FROM Playlist c INNER JOIN OrderedList l ON " + "c.nextListId = l.id ), NameConcat AS ( SELECT child AS id, " + " GROUP_CONCAT(name ,';') || ';' AS path FROM ( SELECT " + "child, name FROM Heirarchy ORDER BY depth DESC ) " + "GROUP BY child ) SELECT id, path, ROW_NUMBER() OVER ( " + " ORDER BY (SELECT COUNT(*) FROM (SELECT * FROM Heirarchy " + "WHERE child = id) ) DESC, (SELECT position FROM OrderedList " + "ol WHERE ol.id = c.id) ASC ) AS position FROM Playlist c LEFT " + "JOIN NameConcat g USING (id);"; + db << "CREATE TRIGGER trigger_before_delete_PlaylistEntity BEFORE DELETE " + "ON PlaylistEntity WHEN OLD.trackId > 0 BEGIN UPDATE " + "PlaylistEntity SET nextEntityId = OLD.nextEntityId WHERE " + "nextEntityId = OLD.id AND listId = OLD.listId; END;"; + db << "CREATE INDEX index_Track_filename ON Track (filename);"; + db << "CREATE INDEX index_Track_albumArtId ON Track (albumArtId);"; + db << "CREATE INDEX index_Track_uri ON Track (uri);"; + db << "CREATE INDEX index_Track_title ON Track(title);"; + db << "CREATE INDEX index_Track_length ON Track(length);"; + db << "CREATE INDEX index_Track_rating ON Track(rating);"; + db << "CREATE INDEX index_Track_year ON Track(year);"; + db << "CREATE INDEX index_Track_dateAdded ON Track(dateAdded);"; + db << "CREATE INDEX index_Track_genre ON Track(genre);"; + db << "CREATE INDEX index_Track_artist ON Track(artist);"; + db << "CREATE INDEX index_Track_album ON Track(album);"; + db << "CREATE INDEX index_Track_key ON Track(key);"; + db << "CREATE INDEX index_Track_bpmAnalyzed ON Track(CAST(bpmAnalyzed + " + "0.5 AS int));"; + db << "CREATE TRIGGER trigger_after_insert_Track_check_id AFTER INSERT ON " + "Track WHEN NEW.id <= (SELECT seq FROM sqlite_sequence WHERE name " + "= 'Track') BEGIN SELECT RAISE(ABORT, 'Recycling deleted track " + "id''s are not allowed'); END;"; + db << "CREATE TRIGGER trigger_after_update_Track_check_Id BEFORE UPDATE ON " + "Track WHEN NEW.id <> OLD.id BEGIN SELECT RAISE(ABORT, " + "'Changing track id''s are not allowed'); END;"; + db << "CREATE TRIGGER trigger_after_insert_Track_fix_origin AFTER INSERT " + "ON Track WHEN IFNULL(NEW.originTrackId, 0) = 0 OR " + "IFNULL(NEW.originDatabaseUuid, '') = '' BEGIN UPDATE Track SET " + " originTrackId = NEW.id, originDatabaseUuid = (SELECT " + "uuid FROM Information) WHERE track.id = NEW.id; END;"; + db << "CREATE TRIGGER trigger_after_update_Track_fix_origin AFTER UPDATE " + "ON Track WHEN IFNULL(NEW.originTrackId, 0) = 0 OR " + "IFNULL(NEW.originDatabaseUuid, '') = '' BEGIN UPDATE Track SET " + " originTrackId = NEW.id, originDatabaseUuid = (SELECT " + "uuid FROM Information) WHERE track.id = NEW.id; END;"; + db << "CREATE TRIGGER trigger_after_update_Track_timestamp AFTER UPDATE " + "OF length, bpm, year, filename, bitrate, bpmAnalyzed, albumArtId, " + " title, artist, album, genre, comment, label, composer, remixer, " + "key, rating, albumArt, fileType, isAnalyzed, isBeatgridLocked, " + "trackData, overviewWaveformData, beatData, quickCues, loops, " + "explicitLyrics, activeOnLoadLoops ON Track FOR EACH ROW BEGIN " + " UPDATE Track SET lastEditTime = strftime('%s') WHERE " + "ROWID=NEW.ROWID; END;"; + db << "CREATE TRIGGER trigger_after_insert_Track_insert_performance_data " + "AFTER INSERT ON Track BEGIN INSERT INTO PerformanceData(trackId) " + "VALUES(NEW.id); END;"; + db << "CREATE INDEX index_PreparelistEntity_trackId ON PreparelistEntity " + "(trackId);"; + + // Generate UUID for the Information table. + auto uuid_str = djinterop::util::generate_random_uuid(); + + // Not yet sure how the "currentPlayedIndiciator" (typo deliberate) value + // is formed. + auto current_played_indicator_fake_value = + djinterop::util::generate_random_int64(); + + // Insert row into Information + db << "INSERT INTO Information ([uuid], [schemaVersionMajor], " + "[schemaVersionMinor], [schemaVersionPatch], " + "[currentPlayedIndiciator], [lastRekordBoxLibraryImportReadCounter]) " + "VALUES (?, ?, ?, ?, ?, ?)" + << uuid_str << schema_version.maj << schema_version.min + << schema_version.pat << current_played_indicator_fake_value << 0; + + // Insert default album art entry + db << "INSERT INTO AlbumArt VALUES (1, '', NULL)"; +} + +} // namespace djinterop::engine::schema diff --git a/src/djinterop/engine/schema/schema_3_0_0.hpp b/src/djinterop/engine/schema/schema_3_0_0.hpp new file mode 100644 index 0000000..55f2d03 --- /dev/null +++ b/src/djinterop/engine/schema/schema_3_0_0.hpp @@ -0,0 +1,45 @@ +/* + 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 . + */ + +#pragma once + +#include + +#include + +#include "schema_2_21_2.hpp" + +namespace djinterop::engine::schema +{ +class schema_3_0_0 : public schema_2_21_2 +{ + // Note that despite having a new major version number, DB schema 3.x is + // sufficiently similar to 2.x to be modelled as an evolution of it rather + // than an entirely new schema. +public: + static constexpr const semantic_version schema_version{3, 0, 0}; + + void verify(sqlite::database& db) const override; + void create(sqlite::database& db) override; + +protected: + void verify_master_list(sqlite::database& db) const override; + virtual void verify_performance_data(sqlite::database& db) const; + void verify_track(sqlite::database& db) const override; +}; + +} // namespace djinterop::engine::schema diff --git a/src/djinterop/engine/v1/engine_crate_impl.cpp b/src/djinterop/engine/v1/engine_crate_impl.cpp index ae26cb3..54d8c6f 100644 --- a/src/djinterop/engine/v1/engine_crate_impl.cpp +++ b/src/djinterop/engine/v1/engine_crate_impl.cpp @@ -145,7 +145,7 @@ crate engine_crate_impl::create_sub_crate(const std::string& name) }; int64_t sub_id; - if (storage_->version >= desktop_1_1_1) + if (storage_->schema >= engine_schema::schema_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 diff --git a/src/djinterop/engine/v1/engine_database_impl.cpp b/src/djinterop/engine/v1/engine_database_impl.cpp index 0acad6d..0757cc9 100644 --- a/src/djinterop/engine/v1/engine_database_impl.cpp +++ b/src/djinterop/engine/v1/engine_database_impl.cpp @@ -95,7 +95,7 @@ crate engine_database_impl::create_root_crate(const std::string& name) djinterop::util::sqlite_transaction trans{storage_->db}; int64_t id; - if (storage_->version.schema_version >= desktop_1_1_1.schema_version) + if (storage_->schema >= engine_schema::schema_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 @@ -145,7 +145,7 @@ std::string engine_database_impl::directory() void engine_database_impl::verify() { auto schema_creator_validator = - schema::make_schema_creator_validator(storage_->version); + schema::make_schema_creator_validator(storage_->schema); schema_creator_validator->verify(storage_->db); } @@ -246,7 +246,7 @@ std::string engine_database_impl::uuid() std::string engine_database_impl::version_name() { - return storage_->version.name; + return to_string(storage_->schema); } } // namespace djinterop::engine::v1 diff --git a/src/djinterop/engine/v1/engine_storage.cpp b/src/djinterop/engine/v1/engine_storage.cpp index afdba3c..6a6d50b 100644 --- a/src/djinterop/engine/v1/engine_storage.cpp +++ b/src/djinterop/engine/v1/engine_storage.cpp @@ -17,6 +17,7 @@ #include "engine_storage.hpp" +#include #include #include @@ -60,46 +61,53 @@ sqlite::database make_temporary_db() return db; } +engine_storage load_existing(const std::string& directory) +{ + auto db = make_attached_db(directory, true); + + const auto schema = schema::detect_schema(db, "music"); + return engine_storage{directory, schema, db}; +} + } // anonymous namespace -engine_storage::engine_storage( - const std::string& directory, const engine_version& version) : - engine_storage{directory, version, make_attached_db(directory, true)} +engine_storage::engine_storage(const std::string& directory) : + engine_storage{load_existing(directory)} { } engine_storage::engine_storage( - const std::string& directory, const engine_version& version, + const std::string& directory, const engine_schema& schema, sqlite::database db) : directory{directory}, - db{std::move(db)}, version{version} + db{std::move(db)}, schema{schema} { } std::shared_ptr engine_storage::create( - const std::string& directory, const engine_version& version) + const std::string& directory, const engine_schema& schema) { auto db = make_attached_db(directory, false); // Create the desired schema on the new database. - auto schema_creator = schema::make_schema_creator_validator(version); + auto schema_creator = schema::make_schema_creator_validator(schema); schema_creator->create(db); return std::shared_ptr{ - new engine_storage{directory, version, std::move(db)}}; + new engine_storage{directory, schema, std::move(db)}}; } std::shared_ptr engine_storage::create_temporary( - const engine_version& version) + const engine_schema& schema) { auto db = make_temporary_db(); // Create the desired schema on the new database. - auto schema_creator = schema::make_schema_creator_validator(version); + auto schema_creator = schema::make_schema_creator_validator(schema); schema_creator->create(db); return std::shared_ptr{ - new engine_storage{":memory:", version, std::move(db)}}; + new engine_storage{":memory:", schema, std::move(db)}}; } int64_t engine_storage::create_track( @@ -118,7 +126,7 @@ int64_t engine_storage::create_track( const std::optional& uri, std::optional is_beatgrid_locked) { - if (version.schema_version >= os_1_6_0.schema_version) + if (schema >= engine_schema::schema_1_18_0_desktop) { db << "INSERT INTO Track (playOrder, length, " "lengthCalculated, bpm, year, path, filename, bitrate, " @@ -141,7 +149,7 @@ int64_t engine_storage::create_track( << uri // Added in 1.15.0 << is_beatgrid_locked; // Added in 1.18.0 } - else if (version.schema_version >= os_1_4_0.schema_version) + else if (schema >= engine_schema::schema_1_15_0) { db << "INSERT INTO Track (playOrder, length, " "lengthCalculated, bpm, year, path, filename, bitrate, " @@ -161,7 +169,7 @@ int64_t engine_storage::create_track( << pdb_import_key // Added in 1.7.1 << uri; // Added in 1.15.0 } - else if (version.schema_version >= os_1_0_3.schema_version) + else if (schema >= engine_schema::schema_1_7_1) { db << "INSERT INTO Track (playOrder, length, " "lengthCalculated, bpm, year, path, filename, bitrate, " @@ -203,7 +211,7 @@ int64_t engine_storage::create_track( track_row engine_storage::get_track(int64_t id) { std::optional result; - if (version.schema_version >= os_1_6_0.schema_version) + if (schema >= engine_schema::schema_1_18_0_desktop) { db << ("SELECT playOrder, length, lengthCalculated, bpm, year, path, " "filename, bitrate, bpmAnalyzed, trackType, isExternalTrack, " @@ -254,7 +262,7 @@ track_row engine_storage::get_track(int64_t id) is_beatgrid_locked}; }; } - else if (version.schema_version >= os_1_4_0.schema_version) + else if (schema >= engine_schema::schema_1_15_0) { db << ("SELECT playOrder, length, lengthCalculated, bpm, year, path, " "filename, bitrate, bpmAnalyzed, trackType, isExternalTrack, " @@ -303,7 +311,7 @@ track_row engine_storage::get_track(int64_t id) std::move(uri)}; }; } - else if (version.schema_version >= os_1_0_3.schema_version) + else if (schema >= engine_schema::schema_1_7_1) { db << ("SELECT playOrder, length, lengthCalculated, bpm, year, path, " "filename, bitrate, bpmAnalyzed, trackType, isExternalTrack, " @@ -416,7 +424,7 @@ void engine_storage::update_track( const std::optional& uri, std::optional is_beatgrid_locked) { - if (version.schema_version >= os_1_6_0.schema_version) + if (schema >= engine_schema::schema_1_18_0_desktop) { db << "UPDATE Track SET " "playOrder = ?, length = ?, lengthCalculated = ?, bpm = ?, " @@ -435,7 +443,7 @@ void engine_storage::update_track( << is_beatgrid_locked // Added in 1.18.0 << id; } - else if (version.schema_version >= os_1_4_0.schema_version) + else if (schema >= engine_schema::schema_1_15_0) { db << "UPDATE Track SET " "playOrder = ?, length = ?, lengthCalculated = ?, bpm = ?, " @@ -453,7 +461,7 @@ void engine_storage::update_track( << uri // Added in 1.15.0 << id; } - else if (version.schema_version >= os_1_0_3.schema_version) + else if (schema >= engine_schema::schema_1_7_1) { db << "UPDATE Track SET " "playOrder = ?, length = ?, lengthCalculated = ?, bpm = ?, " @@ -556,7 +564,7 @@ void engine_storage::set_meta_data( { // Note that rows are created even for null values. std::optional no_value; - if (version.schema_version >= os_1_4_0.schema_version) + if (schema >= engine_schema::schema_1_15_0) { // A new unknown entry of type 17 may appear from 1.15.0 onwards. db << "INSERT OR REPLACE INTO MetaData(id, type, text) VALUES " @@ -694,7 +702,7 @@ void engine_storage::set_meta_data_integer( // order 4, 5, 1, 2, 3, 6, 8, 7, 9, 10, 11, for reasons unknown. The code // below replicates this order for maximum compatibility. std::optional no_value; - if (version.schema_version >= os_1_2_0.schema_version) + if (schema >= engine_schema::schema_1_11_1) { // A new unknown entry of type 12 may appear from 1.11.1 onwards. db << "INSERT OR REPLACE INTO MetaDataInteger (id, type, value) VALUES " @@ -774,7 +782,7 @@ void engine_storage::clear_performance_data(int64_t id) performance_data_row engine_storage::get_performance_data(int64_t id) { std::optional result; - if (version.schema_version >= os_1_2_0.schema_version) + if (schema >= engine_schema::schema_1_11_1) { db << "SELECT id, isAnalyzed, isRendered, " "trackData, highResolutionWaveFormData, " @@ -812,7 +820,7 @@ performance_data_row engine_storage::get_performance_data(int64_t id) has_traktor_values}; }; } - else if (version.schema_version >= os_1_0_3.schema_version) + else if (schema >= engine_schema::schema_1_7_1) { db << "SELECT id, isAnalyzed, isRendered, " "trackData, highResolutionWaveFormData, " @@ -906,7 +914,7 @@ void engine_storage::set_performance_data( { // TODO (mr-smidge): check encoding/decoding invariants. - if (version.schema_version >= os_1_2_0.schema_version) + if (schema >= engine_schema::schema_1_11_1) { db << "INSERT OR REPLACE INTO PerformanceData (" "id, isAnalyzed, isRendered, " @@ -923,7 +931,7 @@ void engine_storage::set_performance_data( << loops_data.encode() << has_serato_values << has_rekordbox_values << has_traktor_values; } - else if (version.schema_version >= os_1_0_3.schema_version) + else if (schema >= engine_schema::schema_1_7_1) { db << "INSERT OR REPLACE INTO PerformanceData (" "id, isAnalyzed, isRendered, " diff --git a/src/djinterop/engine/v1/engine_storage.hpp b/src/djinterop/engine/v1/engine_storage.hpp index 444a71f..d2b7b6b 100644 --- a/src/djinterop/engine/v1/engine_storage.hpp +++ b/src/djinterop/engine/v1/engine_storage.hpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include "djinterop/engine/metadata_types.hpp" @@ -99,18 +99,22 @@ class engine_storage { public: /// Construct by loading from an existing DB directory. - engine_storage(const std::string& directory, const engine_version& version); + engine_storage(const std::string& directory); + + engine_storage( + const std::string& directory, const engine_schema& schema, + sqlite::database db); /// Make a new, empty DB of a given version. static std::shared_ptr create( - const std::string& directory, const engine_version& version); + const std::string& directory, const engine_schema& schema); /// Make a new, empty, in-memory DB of a given version. /// /// Any changes made to the database will not persist beyond destruction /// of the class instance. static std::shared_ptr create_temporary( - const engine_version& version); + const engine_schema& schema); /// Create an entry in the `Track` table. int64_t create_track( @@ -328,7 +332,7 @@ class engine_storage << loops_data{}.encode() // << 0.0; // hasSeratoValues - if (version.schema_version >= os_1_0_3.schema_version) + if (schema >= engine_schema::schema_1_7_1) { db << "UPDATE PerformanceData SET hasRekordboxValues = 0 " "WHERE id = ?" @@ -347,17 +351,12 @@ class engine_storage /// SQLite database handle, with both music and performance DBs attached. sqlite::database db; - /// The version of the Engine database. - const engine_version version; + /// The schema version of the Engine database. + const engine_schema schema; /// Pointer to the schema creator/validator. const std::unique_ptr schema_creator_validator; - -private: - engine_storage( - const std::string& directory, const engine_version& version, - sqlite::database db); }; } // namespace djinterop::engine::v1 diff --git a/src/djinterop/engine/v2/change_log_table.cpp b/src/djinterop/engine/v2/change_log_table.cpp index e6a66c1..7779159 100644 --- a/src/djinterop/engine/v2/change_log_table.cpp +++ b/src/djinterop/engine/v2/change_log_table.cpp @@ -30,7 +30,7 @@ change_log_table::change_log_table( std::shared_ptr context) : context_{std::move(context)} { - if (context_->version.schema_version >= semantic_version{2, 20, 3}) + if (context_->schema >= engine_schema::schema_2_20_3) { throw djinterop::unsupported_operation{ "The ChangeLog table is removed in firmware 3.0 and above"}; diff --git a/src/djinterop/engine/v2/database_impl.cpp b/src/djinterop/engine/v2/database_impl.cpp index 6a3a8b1..23a38e2 100644 --- a/src/djinterop/engine/v2/database_impl.cpp +++ b/src/djinterop/engine/v2/database_impl.cpp @@ -219,7 +219,7 @@ std::string database_impl::uuid() std::string database_impl::version_name() { - return library_->version().name; + return to_string(library_->schema()); } } // namespace djinterop::engine::v2 diff --git a/src/djinterop/engine/v2/engine_library.cpp b/src/djinterop/engine/v2/engine_library.cpp index a39b2df..6edd1b9 100644 --- a/src/djinterop/engine/v2/engine_library.cpp +++ b/src/djinterop/engine/v2/engine_library.cpp @@ -54,35 +54,8 @@ std::shared_ptr load_existing( sqlite::database db{db_path}; - // Check that the `Information` table exists. - std::string sql = - "SELECT COUNT(*) AS rows " - "FROM sqlite_master " - "WHERE name = 'Information'"; - int32_t table_count; - db << sql >> table_count; - if (table_count != 1) - { - throw database_inconsistency{ - "Did not find an `Information` table in the database"}; - } - - // Detect version. - semantic_version schema_version{}; - db << "SELECT schemaVersionMajor, schemaVersionMinor, " - "schemaVersionPatch FROM Information" >> - std::tie(schema_version.maj, schema_version.min, schema_version.pat); - - for (auto&& candidate_version : all_v2_versions) - { - if (schema_version == candidate_version.schema_version) - { - return std::make_shared( - directory, candidate_version, db); - } - } - - throw unsupported_engine_database{schema_version}; + const auto schema = schema::detect_schema(db); + return std::make_shared(directory, schema, db); } } // anonymous namespace @@ -99,7 +72,7 @@ engine_library::engine_library( } engine_library engine_library::create( - const std::string& directory, const engine_version& version) + const std::string& directory, const engine_schema& schema) { // Ensure the target directory exists. if (!djinterop::util::path_exists(directory)) @@ -124,23 +97,23 @@ engine_library engine_library::create( auto db = sqlite::database{db_path}; - auto schema_creator = schema::make_schema_creator_validator(version); + auto schema_creator = schema::make_schema_creator_validator(schema); schema_creator->create(db); return engine_library{std::make_shared( - directory, version, std::move(db))}; + directory, schema, std::move(db))}; } -engine_library engine_library::create_temporary(const engine_version& version) +engine_library engine_library::create_temporary(const engine_schema& schema) { auto db = sqlite::database(":memory:"); // Create the desired schema on the new database. - auto schema_creator = schema::make_schema_creator_validator(version); + auto schema_creator = schema::make_schema_creator_validator(schema); schema_creator->create(db); return engine_library{std::make_shared( - ":memory:", version, std::move(db))}; + ":memory:", schema, std::move(db))}; } bool engine_library::exists(const std::string& directory) @@ -150,7 +123,8 @@ bool engine_library::exists(const std::string& directory) void engine_library::verify() const { - auto validator = schema::make_schema_creator_validator(context_->version); + auto validator = + schema::make_schema_creator_validator(context_->schema); validator->verify(context_->db); } @@ -166,9 +140,9 @@ std::string engine_library::directory() const return context_->directory; } -engine_version engine_library::version() const +engine_schema engine_library::schema() const { - return context_->version; + return context_->schema; } } // namespace djinterop::engine::v2 diff --git a/src/djinterop/engine/v2/engine_library_context.hpp b/src/djinterop/engine/v2/engine_library_context.hpp index fb3bdef..40aef86 100644 --- a/src/djinterop/engine/v2/engine_library_context.hpp +++ b/src/djinterop/engine/v2/engine_library_context.hpp @@ -21,24 +21,23 @@ #include -#include -#include +#include namespace djinterop::engine::v2 { struct engine_library_context { engine_library_context( - std::string directory, engine_version version, sqlite::database db) : - directory{std::move(directory)}, version{version}, db{std::move(db)} + std::string directory, engine_schema schema, sqlite::database db) : + directory{std::move(directory)}, schema{schema}, db{std::move(db)} { } /// The directory in which the Engine DB files reside. const std::string directory; - /// The version of the Engine database. - const engine_version version; + /// The schema version of the Engine database. + const engine_schema schema; /// The main SQLite database holding Engine data. sqlite::database db; diff --git a/src/djinterop/engine/v2/track_table.cpp b/src/djinterop/engine/v2/track_table.cpp index 5487d89..d97fa1c 100644 --- a/src/djinterop/engine/v2/track_table.cpp +++ b/src/djinterop/engine/v2/track_table.cpp @@ -110,7 +110,7 @@ int64_t track_table::add(const track_row& row) "and so it cannot be created again"}; } - if (context_->version.schema_version >= semantic_version{2, 20, 3}) + if (context_->schema >= engine_schema::schema_2_20_3) { context_->db << "INSERT INTO Track (" "playOrder, length, bpm, year, " @@ -182,7 +182,7 @@ int64_t track_table::add(const track_row& row) << row.active_on_load_loops << djinterop::util::to_timestamp(row.last_edit_time); } - else if (context_->version.schema_version >= semantic_version{2, 20, 1}) + else if (context_->schema >= engine_schema::schema_2_20_1) { context_->db << "INSERT INTO Track (" "playOrder, length, bpm, year, " @@ -354,7 +354,7 @@ std::optional track_table::get(int64_t id) const { std::optional result; - if (context_->version.schema_version >= semantic_version{2, 20, 3}) + if (context_->schema >= engine_schema::schema_2_20_3) { context_->db << "SELECT id, playOrder, length, bpm, year, path, filename, " @@ -462,7 +462,7 @@ std::optional track_table::get(int64_t id) const djinterop::util::to_time_point(last_edit_time)}; }; } - else if (context_->version.schema_version >= semantic_version{2, 20, 1}) + else if (context_->schema >= engine_schema::schema_2_20_1) { context_->db << "SELECT id, playOrder, length, bpm, year, path, filename, " @@ -713,7 +713,7 @@ void track_table::update(const track_row& row) "The track row to update does not contain a track id"}; } - if (context_->version.schema_version >= semantic_version{2, 20, 3}) + if (context_->schema >= engine_schema::schema_2_20_3) { context_->db << "UPDATE Track SET " "playOrder = ?, length = ?, bpm = ?, year = ?, " @@ -763,7 +763,7 @@ void track_table::update(const track_row& row) << djinterop::util::to_timestamp(row.last_edit_time) << row.id; } - else if (context_->version.schema_version >= semantic_version{2, 20, 1}) + else if (context_->schema >= engine_schema::schema_2_20_1) { context_->db << "UPDATE Track SET " "playOrder = ?, length = ?, bpm = ?, year = ?, " @@ -1388,7 +1388,7 @@ void track_table::set_explicit_lyrics(int64_t id, bool explicit_lyrics) std::optional track_table::get_active_on_load_loops(int64_t id) { - if (context_->version.schema_version < semantic_version{2, 20, 1}) + if (context_->schema < engine_schema::schema_2_20_1) throw djinterop::unsupported_operation{ "The `activeOnLoadLoops` column is not available for this " "database version"}; @@ -1400,7 +1400,7 @@ std::optional track_table::get_active_on_load_loops(int64_t id) void track_table::set_active_on_load_loops( int64_t id, std::optional active_on_load_loops) { - if (context_->version.schema_version < semantic_version{2, 20, 1}) + if (context_->schema < engine_schema::schema_2_20_1) throw djinterop::unsupported_operation{ "The `activeOnLoadLoops` column is not available for this " "database version"}; @@ -1412,7 +1412,7 @@ void track_table::set_active_on_load_loops( std::chrono::system_clock::time_point track_table::get_last_edit_time( int64_t id) { - if (context_->version.schema_version < semantic_version{2, 20, 3}) + if (context_->schema < engine_schema::schema_2_20_3) throw djinterop::unsupported_operation{ "The `lastEditTime` column is not available for this " "database version"}; @@ -1424,7 +1424,7 @@ std::chrono::system_clock::time_point track_table::get_last_edit_time( void track_table::set_last_edit_time( int64_t id, std::chrono::system_clock::time_point last_edit_time) { - if (context_->version.schema_version < semantic_version{2, 20, 3}) + if (context_->schema < engine_schema::schema_2_20_3) throw djinterop::unsupported_operation{ "The `lastEditTime` column is not available for this " "database version"}; diff --git a/test/djinterop/engine/crate_test.cpp b/test/djinterop/engine/crate_test.cpp index d5f8f8a..e90011c 100644 --- a/test/djinterop/engine/crate_test.cpp +++ b/test/djinterop/engine/crate_test.cpp @@ -3,7 +3,7 @@ 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 + the Free Software Foundation, either schema 3 of the License, or (at your option) any later version. libdjinterop is distributed in the hope that it will be useful, @@ -41,16 +41,16 @@ std::vector invalid_crate_names{"", "Contains ; semicolon"}; BOOST_TEST_DECORATOR( *utf::description("crate::crate() for all supported schema versions")) -BOOST_DATA_TEST_CASE(ctor_copy__copies, e::all_versions, version) +BOOST_DATA_TEST_CASE(ctor_copy__copies, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Copying crate via copy ctor..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Copying crate via copy ctor..."); djinterop::crate copy{crate}; // Assert @@ -60,16 +60,16 @@ BOOST_DATA_TEST_CASE(ctor_copy__copies, e::all_versions, version) BOOST_TEST_DECORATOR( *utf::description("crate::operator=() for all supported schema versions")) -BOOST_DATA_TEST_CASE(op_copy_assign__copies, e::all_versions, version) +BOOST_DATA_TEST_CASE(op_copy_assign__copies, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Copying crate via copy ctor..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Copying crate via copy ctor..."); auto copy = crate; // Assert @@ -79,23 +79,23 @@ BOOST_DATA_TEST_CASE(op_copy_assign__copies, e::all_versions, version) BOOST_TEST_DECORATOR(*utf::description( "crate::add_track() to empty crate for all supported schema versions")) -BOOST_DATA_TEST_CASE(add_track__to_empty_crate__adds, e::all_versions, version) +BOOST_DATA_TEST_CASE(add_track__to_empty_crate__adds, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::minimal_1, - example_track_data_usage::create, version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example track..."); + example_track_data_usage::create, schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example track..."); auto track = db.create_track(snapshot); // Act BOOST_TEST_CHECKPOINT( - "(" << version << ") Adding example track to crate..."); + "(" << schema << ") Adding example track to crate..."); crate.add_track(track); // Assert @@ -108,22 +108,22 @@ BOOST_DATA_TEST_CASE(add_track__to_empty_crate__adds, e::all_versions, version) BOOST_TEST_DECORATOR(*utf::description( "crate::add_track() to non-empty crate for all supported schema versions")) BOOST_DATA_TEST_CASE( - add_track__to_nonempty_crate__adds, e::all_versions, version) + add_track__to_nonempty_crate__adds, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); djinterop::track_snapshot snapshot1{}; djinterop::track_snapshot snapshot2{}; populate_track_snapshot( snapshot1, example_track_data_variation::minimal_1, - example_track_data_usage::create, version); + example_track_data_usage::create, schema); populate_track_snapshot( snapshot2, example_track_data_variation::basic_metadata_only_1, - example_track_data_usage::create, version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example tracks..."); + example_track_data_usage::create, schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example tracks..."); auto track1 = db.create_track(snapshot1); auto track2 = db.create_track(snapshot2); crate.add_track(track1); @@ -145,22 +145,22 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("crate::add_tracks() for all supported schema versions")) -BOOST_DATA_TEST_CASE(add_tracks__adds, e::all_versions, version) +BOOST_DATA_TEST_CASE(add_tracks__adds, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); djinterop::track_snapshot snapshot1{}; djinterop::track_snapshot snapshot2{}; populate_track_snapshot( snapshot1, example_track_data_variation::minimal_1, - example_track_data_usage::create, version); + example_track_data_usage::create, schema); populate_track_snapshot( snapshot2, example_track_data_variation::basic_metadata_only_1, - example_track_data_usage::create, version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example tracks..."); + example_track_data_usage::create, schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example tracks..."); auto track1 = db.create_track(snapshot1); auto track2 = db.create_track(snapshot2); std::vector tracks{track1, track2}; @@ -175,12 +175,12 @@ BOOST_DATA_TEST_CASE(add_tracks__adds, e::all_versions, version) BOOST_TEST_DECORATOR(*utf::description( "crate::children() on empty crate for all supported schema versions")) -BOOST_DATA_TEST_CASE(children__empty__none, e::all_versions, version) +BOOST_DATA_TEST_CASE(children__empty__none, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); // Act @@ -192,12 +192,12 @@ BOOST_DATA_TEST_CASE(children__empty__none, e::all_versions, version) BOOST_TEST_DECORATOR(*utf::description( "crate::clear_tracks() on empty crate for all supported schema versions")) -BOOST_DATA_TEST_CASE(clear_tracks__empty__no_effect, e::all_versions, version) +BOOST_DATA_TEST_CASE(clear_tracks__empty__no_effect, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); // Act @@ -211,21 +211,21 @@ BOOST_DATA_TEST_CASE(clear_tracks__empty__no_effect, e::all_versions, version) BOOST_TEST_DECORATOR( *utf::description("crate::clear_tracks() on non-empty crate for all " "supported schema versions")) -BOOST_DATA_TEST_CASE(clear_tracks__nonempty__cleared, e::all_versions, version) +BOOST_DATA_TEST_CASE(clear_tracks__nonempty__cleared, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::minimal_1, - example_track_data_usage::create, version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example track..."); + example_track_data_usage::create, schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example track..."); auto track = db.create_track(snapshot); BOOST_TEST_CHECKPOINT( - "(" << version << ") Adding example track to crate..."); + "(" << schema << ") Adding example track to crate..."); crate.add_track(track); // Act @@ -238,16 +238,16 @@ BOOST_DATA_TEST_CASE(clear_tracks__nonempty__cleared, e::all_versions, version) BOOST_TEST_DECORATOR(*utf::description( "crate::create_sub_crate() for all supported schema versions")) -BOOST_DATA_TEST_CASE(create_sub_crate__creates, e::all_versions, version) +BOOST_DATA_TEST_CASE(create_sub_crate__creates, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example sub crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example sub crate..."); auto sub_crate = crate.create_sub_crate("Example Sub Crate"); // Assert @@ -261,23 +261,23 @@ BOOST_TEST_DECORATOR( *utf::description("crate::remove_track() for track not in crate for all " "supported schema versions")) BOOST_DATA_TEST_CASE( - remove_track__not_in_crate__no_effect, e::all_versions, version) + remove_track__not_in_crate__no_effect, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::minimal_1, - example_track_data_usage::create, version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example track..."); + example_track_data_usage::create, schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example track..."); auto track = db.create_track(snapshot); // Act BOOST_TEST_CHECKPOINT( - "(" << version << ") Attempting to remove track from crate..."); + "(" << schema << ") Attempting to remove track from crate..."); crate.remove_track(track); // Assert @@ -288,25 +288,25 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("crate::remove_track() for track in crate for all " "supported schema versions")) -BOOST_DATA_TEST_CASE(remove_track__in_crate__removed, e::all_versions, version) +BOOST_DATA_TEST_CASE(remove_track__in_crate__removed, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::minimal_1, - example_track_data_usage::create, version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example track..."); + example_track_data_usage::create, schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example track..."); auto track = db.create_track(snapshot); BOOST_TEST_CHECKPOINT( - "(" << version << ") Adding example track to crate..."); + "(" << schema << ") Adding example track to crate..."); crate.add_track(track); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Removing track from crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Removing track from crate..."); crate.remove_track(track); // Assert @@ -317,17 +317,17 @@ BOOST_DATA_TEST_CASE(remove_track__in_crate__removed, e::all_versions, version) BOOST_TEST_DECORATOR(*utf::description( "crate::set_name() with valid name for all supported schema versions")) BOOST_DATA_TEST_CASE( - set_name__valid__sets, e::all_versions* valid_crate_names, version, + set_name__valid__sets, e::supported_schemas* valid_crate_names, schema, crate_name) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Changing crate name..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Changing crate name..."); crate.set_name(crate_name); // Assert @@ -337,17 +337,17 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(*utf::description( "crate::set_name() with invalid name for all supported schema versions")) BOOST_DATA_TEST_CASE( - set_name__invalid__throws, e::all_versions* invalid_crate_names, version, + set_name__invalid__throws, e::supported_schemas* invalid_crate_names, schema, crate_name) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); // Act/Assert - BOOST_TEST_CHECKPOINT("(" << version << ") Changing crate name..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Changing crate name..."); BOOST_CHECK_THROW( crate.set_name(crate_name), djinterop::crate_invalid_name); } @@ -355,16 +355,16 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(*utf::description( "crate::set_parent() from root to root for all supported schema versions")) BOOST_DATA_TEST_CASE( - set_parent__root_to_root__no_effect, e::all_versions, version) + set_parent__root_to_root__no_effect, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Moveable Crate"); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Setting parent..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Setting parent..."); crate.set_parent(std::nullopt); // Assert @@ -375,19 +375,19 @@ BOOST_TEST_DECORATOR( *utf::description("crate::set_parent() from root to non-root for all " "supported schema versions")) BOOST_DATA_TEST_CASE( - set_parent__root_to_nonroot__changes, e::all_versions, version) + set_parent__root_to_nonroot__changes, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Moveable Crate"); BOOST_TEST_CHECKPOINT( - "(" << version << ") Creating another example crate..."); + "(" << schema << ") Creating another example crate..."); auto new_parent_crate = db.create_root_crate("Example Root Crate"); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Setting parent..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Setting parent..."); crate.set_parent(new_parent_crate); // Assert @@ -399,18 +399,18 @@ BOOST_TEST_DECORATOR( *utf::description("crate::set_parent() from non-root to root for all " "supported schema versions")) BOOST_DATA_TEST_CASE( - set_parent__nonroot_to_root__changes, e::all_versions, version) + set_parent__nonroot_to_root__changes, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto original_parent_crate = db.create_root_crate("Example Root Crate"); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example sub crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example sub crate..."); auto crate = original_parent_crate.create_sub_crate("Moveable Crate"); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Setting parent..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Setting parent..."); crate.set_parent(std::nullopt); // Assert @@ -421,21 +421,21 @@ BOOST_TEST_DECORATOR( *utf::description("crate::set_parent() from non-root to non-root for all " "supported schema versions")) BOOST_DATA_TEST_CASE( - set_parent__nonroot_to_nonroot__changes, e::all_versions, version) + set_parent__nonroot_to_nonroot__changes, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto original_parent_crate = db.create_root_crate("Example Root Crate"); BOOST_TEST_CHECKPOINT( - "(" << version << ") Creating another example crate..."); + "(" << schema << ") Creating another example crate..."); auto new_parent_crate = db.create_root_crate("Another Example Root Crate"); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example sub crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example sub crate..."); auto crate = original_parent_crate.create_sub_crate("Moveable Crate"); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Setting parent..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Setting parent..."); crate.set_parent(new_parent_crate); // Assert @@ -445,16 +445,16 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(*utf::description( "crate::set_parent() to self for all supported schema versions")) -BOOST_DATA_TEST_CASE(set_parent__self__throws, e::all_versions, version) +BOOST_DATA_TEST_CASE(set_parent__self__throws, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); // Act/Assert - BOOST_TEST_CHECKPOINT("(" << version << ") Setting parent..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Setting parent..."); BOOST_CHECK_THROW(crate.set_parent(crate), djinterop::crate_invalid_parent); } @@ -465,14 +465,14 @@ BOOST_DATA_TEST_CASE(set_parent__self__throws, e::all_versions, version) BOOST_TEST_DECORATOR( *utf::description("crate::sub_crate_by_name() for extant name for all " "supported schema versions")) -BOOST_DATA_TEST_CASE(sub_crate_by_name__valid__finds, e::all_versions, version) +BOOST_DATA_TEST_CASE(sub_crate_by_name__valid__finds, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example sub-crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example sub-crate..."); auto sub_crate = crate.create_sub_crate("Example Sub Crate"); // Act @@ -487,14 +487,14 @@ BOOST_DATA_TEST_CASE(sub_crate_by_name__valid__finds, e::all_versions, version) BOOST_TEST_DECORATOR( *utf::description("crate::sub_crate_by_name() for not-found name for all " "supported schema versions")) -BOOST_DATA_TEST_CASE(sub_crate_by_name__invalid__none, e::all_versions, version) +BOOST_DATA_TEST_CASE(sub_crate_by_name__invalid__none, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example crate..."); auto crate = db.create_root_crate("Example Root Crate"); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating example sub-crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating example sub-crate..."); auto sub_crate = crate.create_sub_crate("Example Sub Crate"); // Act diff --git a/test/djinterop/engine/database_reference_test.cpp b/test/djinterop/engine/database_reference_test.cpp index dd6dc7c..1439339 100644 --- a/test/djinterop/engine/database_reference_test.cpp +++ b/test/djinterop/engine/database_reference_test.cpp @@ -95,6 +95,7 @@ const std::vector ref_script_dirs{ "/ref/engine/sc5000/firmware-4.0.0", "/ref/engine/desktop/desktop-4.0.1", "/ref/engine/sc5000/firmware-4.0.1", + "/ref/engine/desktop/desktop-4.1.0", }; } // anonymous namespace diff --git a/test/djinterop/engine/database_test.cpp b/test/djinterop/engine/database_test.cpp index 61d69dd..c0864c3 100644 --- a/test/djinterop/engine/database_test.cpp +++ b/test/djinterop/engine/database_test.cpp @@ -3,7 +3,7 @@ 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 + the Free Software Foundation, either schema 3 of the License, or (at your option) any later version. libdjinterop is distributed in the hope that it will be useful, @@ -40,14 +40,14 @@ namespace e = djinterop::engine; BOOST_TEST_DECORATOR(* utf::description( "database::create_root_crate() for all supported schema versions")) BOOST_DATA_TEST_CASE( - create_root_crate__supported_version__creates, e::all_versions, version) + create_root_crate__supported_version__creates, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Creating root crate..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating root crate..."); auto crate = db.create_root_crate("Example Root Crate"); // Assert @@ -58,19 +58,19 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(* utf::description( "database::create_root_crate_after() for all supported schema versions")) BOOST_DATA_TEST_CASE( - create_root_crate_after__supported_version__creates, e::all_v2_versions, - version) + create_root_crate_after__supported_version__creates, e::supported_v2_schemas, + schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); auto crate_a = db.create_root_crate("Example Root Crate A"); auto crate_b = db.create_root_crate("Example Root Crate B"); auto crate_d = db.create_root_crate("Example Root Crate D"); auto crate_e = db.create_root_crate("Example Root Crate E"); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Creating root crate after another..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating root crate after another..."); auto crate = db.create_root_crate_after("Example Root Crate C", crate_b); // Assert @@ -86,19 +86,19 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(* utf::description( "database::create_track() for all supported schema versions")) BOOST_DATA_TEST_CASE( - create_track__supported_version__creates, e::all_versions, version) + create_track__supported_version__creates, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::minimal_1, - example_track_data_usage::create, version); + example_track_data_usage::create, schema); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Creating track..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating track..."); auto track = db.create_track(snapshot); // Assert @@ -108,18 +108,18 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(* utf::description( "database::remove_track() for all supported schema versions")) BOOST_DATA_TEST_CASE( - remove_track__supported_version__removes, e::all_versions, version) + remove_track__supported_version__removes, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::minimal_1, - example_track_data_usage::create, version); + example_track_data_usage::create, schema); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating track..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating track..."); auto track = db.create_track(snapshot); // Act @@ -132,34 +132,34 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(* utf::description( "database::verify() for all supported versions")) -BOOST_DATA_TEST_CASE(verify__no_throw, e::all_versions, version) +BOOST_DATA_TEST_CASE(verify__no_throw, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); // Act/Assert - BOOST_TEST_CHECKPOINT("(" << version << ") Verifying DB..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Verifying DB..."); db.verify(); } BOOST_TEST_DECORATOR(*utf::description("database::tracks(), all schema versions")) -BOOST_DATA_TEST_CASE(tracks__expected_ids, e::all_versions, version) +BOOST_DATA_TEST_CASE(tracks__expected_ids, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::minimal_1, - example_track_data_usage::create, version); + example_track_data_usage::create, schema); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating track..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating track..."); auto track = db.create_track(snapshot); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Fetching tracks..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Fetching tracks..."); auto results = db.tracks(); // Assert @@ -168,22 +168,22 @@ BOOST_DATA_TEST_CASE(tracks__expected_ids, e::all_versions, version) } BOOST_TEST_DECORATOR(*utf::description("database::tracks_by_relative_path(), valid path, all schema versions")) -BOOST_DATA_TEST_CASE(tracks_by_relative_path__valid_path__expected_ids, e::all_versions, version) +BOOST_DATA_TEST_CASE(tracks_by_relative_path__valid_path__expected_ids, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::minimal_1, - example_track_data_usage::create, version); + example_track_data_usage::create, schema); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating track..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating track..."); auto track = db.create_track(snapshot); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Fetching tracks..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Fetching tracks..."); auto results = db.tracks_by_relative_path(*snapshot.relative_path); // Assert @@ -192,14 +192,14 @@ BOOST_DATA_TEST_CASE(tracks_by_relative_path__valid_path__expected_ids, e::all_v } BOOST_TEST_DECORATOR(*utf::description("database::tracks_by_relative_path(), invalid path, all schema versions")) -BOOST_DATA_TEST_CASE(tracks_by_relative_path__invalid_path__no_ids, e::all_versions, version) +BOOST_DATA_TEST_CASE(tracks_by_relative_path__invalid_path__no_ids, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Fetching tracks..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Fetching tracks..."); auto results = db.tracks_by_relative_path("Does Not Exist.mp3"); // Assert @@ -207,22 +207,22 @@ BOOST_DATA_TEST_CASE(tracks_by_relative_path__invalid_path__no_ids, e::all_versi } BOOST_TEST_DECORATOR(*utf::description("database::track_by_id(), valid id, all schema versions")) -BOOST_DATA_TEST_CASE(track_by_id__valid_id__expected_ids, e::all_versions, version) +BOOST_DATA_TEST_CASE(track_by_id__valid_id__expected_ids, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::minimal_1, - example_track_data_usage::create, version); + example_track_data_usage::create, schema); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating track..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating track..."); auto track = db.create_track(snapshot); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Fetching tracks..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Fetching tracks..."); auto result = db.track_by_id(track.id()); // Assert @@ -231,14 +231,14 @@ BOOST_DATA_TEST_CASE(track_by_id__valid_id__expected_ids, e::all_versions, versi } BOOST_TEST_DECORATOR(*utf::description("database::track_by_id(), invalid id, all schema versions")) -BOOST_DATA_TEST_CASE(track_by_id__invalid_id__no_ids, e::all_versions, version) +BOOST_DATA_TEST_CASE(track_by_id__invalid_id__no_ids, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Fetching tracks..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Fetching tracks..."); auto result = db.track_by_id(123); // Assert diff --git a/test/djinterop/engine/engine_test.cpp b/test/djinterop/engine/engine_test.cpp index edd4240..8284ac2 100644 --- a/test/djinterop/engine/engine_test.cpp +++ b/test/djinterop/engine/engine_test.cpp @@ -22,6 +22,7 @@ #include #include +#include #include "../temporary_directory.hpp" @@ -31,14 +32,15 @@ namespace e = djinterop::engine; BOOST_TEST_DECORATOR( *utf::description("create_database() with all supported schema versions")) BOOST_DATA_TEST_CASE( - create_database__valid_version__creates_verified, e::all_versions, version) + create_database__valid_version__creates_verified, e::supported_schemas, + schema) { // Note separate scope to ensure no locks are held on the temporary dir. temporary_directory tmp_loc; { // Arrange/Act - auto db = e::create_database(tmp_loc.temp_dir, version); + auto db = e::create_database(tmp_loc.temp_dir, schema); // Assert BOOST_CHECK_NO_THROW(db.verify()); @@ -55,7 +57,7 @@ BOOST_AUTO_TEST_CASE(load_database__fake_path__throw) { // Arrange/Act/Assert - e::engine_version unused{}; + e::engine_schema unused{}; BOOST_CHECK_THROW( auto db = e::load_database(tmp_loc.temp_dir + "/does_not_exist", unused), diff --git a/test/djinterop/engine/example_track_data.hpp b/test/djinterop/engine/example_track_data.hpp index 4f161c7..b5d08ec 100644 --- a/test/djinterop/engine/example_track_data.hpp +++ b/test/djinterop/engine/example_track_data.hpp @@ -75,7 +75,7 @@ inline std::ostream& operator<<(std::ostream& o, example_track_data_usage v) inline void populate_track_snapshot( djinterop::track_snapshot& s, example_track_data_variation variation, - example_track_data_usage usage, djinterop::engine::engine_version version) + example_track_data_usage usage, djinterop::engine::engine_schema schema) { using namespace std::string_literals; switch (variation) @@ -193,8 +193,7 @@ inline void populate_track_snapshot( s.comment = "Other Comment"s; s.composer = "Other Composer"s; s.duration = std::chrono::milliseconds{365000}; - if (version.schema_version >= - djinterop::engine::os_1_4_0.schema_version) + if (schema >= djinterop::engine::engine_schema::schema_1_15_0) { s.file_bytes = 1048576; } @@ -229,7 +228,7 @@ inline void populate_track_snapshot( s.track_number = 2; s.waveform.clear(); auto waveform_extents = - version.is_v2_schema() + schema >= djinterop::engine::engine_schema::schema_2_18_0 ? djinterop::engine::calculate_overview_waveform_extents( *s.sample_count, *s.sample_rate) : djinterop::engine:: @@ -238,7 +237,7 @@ inline void populate_track_snapshot( s.waveform.reserve(waveform_extents.size); for (unsigned long long i = 0; i < waveform_extents.size; ++i) { - if (version.schema_version.maj < 2) + if (schema < djinterop::engine::engine_schema::schema_2_18_0) { s.waveform.push_back( {{(uint8_t)(i * 255 / waveform_extents.size), diff --git a/test/djinterop/engine/track_test.cpp b/test/djinterop/engine/track_test.cpp index 6bc8475..89fa97e 100644 --- a/test/djinterop/engine/track_test.cpp +++ b/test/djinterop/engine/track_test.cpp @@ -3,7 +3,7 @@ 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 + the Free Software Foundation, either schema 3 of the License, or (at your option) any later version. libdjinterop is distributed in the hope that it will be useful, @@ -83,22 +83,22 @@ const std::vector updatable_snapshot_type_pairs{ BOOST_TEST_DECORATOR(*utf::description("copy constructor, all schema versions")) BOOST_DATA_TEST_CASE( - ctor__supported_version_copy__copies, e::all_versions, version) + ctor__supported_version_copy__copies, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::fully_analysed_1, - example_track_data_usage::create, version); + example_track_data_usage::create, schema); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating track..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating track..."); auto track = db.create_track(snapshot); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Copying track..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Copying track..."); djinterop::track copy{track}; // Assert @@ -107,22 +107,22 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(*utf::description("copy assignment, all schema versions")) BOOST_DATA_TEST_CASE( - op_copy_assign__supported_version_copy__copies, e::all_versions, version) + op_copy_assign__supported_version_copy__copies, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::fully_analysed_1, - example_track_data_usage::create, version); + example_track_data_usage::create, schema); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating track..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating track..."); auto track = db.create_track(snapshot); // Act - BOOST_TEST_CHECKPOINT("(" << version << ") Copying track..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Copying track..."); djinterop::track copy = track; // Assert @@ -134,33 +134,32 @@ BOOST_TEST_DECORATOR( "versions, all snapshots")) BOOST_DATA_TEST_CASE( snapshot__supported_version__same, - e::all_versions* creatable_snapshot_types, version, snapshot_type) - //foo_versions * foo_snap_types, version, snapshot_type) + e::supported_schemas * creatable_snapshot_types, schema, snapshot_type) { // Arrange BOOST_TEST_CHECKPOINT( - "(" << version << ", " << snapshot_type + "(" << schema << ", " << snapshot_type << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + auto db = e::create_temporary_database(schema); djinterop::track_snapshot snapshot{}; populate_track_snapshot( - snapshot, snapshot_type, example_track_data_usage::create, version); + snapshot, snapshot_type, example_track_data_usage::create, schema); BOOST_TEST_CHECKPOINT( - "(" << version << ", " << snapshot_type << ") Creating track..."); + "(" << schema << ", " << snapshot_type << ") Creating track..."); auto track = db.create_track(snapshot); // Act BOOST_TEST_CHECKPOINT( - "(" << version << ", " << snapshot_type + "(" << schema << ", " << snapshot_type << ") Fetching track snapshot..."); auto actual = track.snapshot(); // Assert djinterop::track_snapshot expected{}; populate_track_snapshot( - expected, snapshot_type, example_track_data_usage::fetch, version); + expected, snapshot_type, example_track_data_usage::fetch, schema); BOOST_CHECK_EQUAL(expected, actual); } @@ -169,32 +168,32 @@ BOOST_TEST_DECORATOR( "all schema versions, all snapshot combinations")) BOOST_DATA_TEST_CASE( update__supported_version__updates, - e::all_versions* updatable_snapshot_type_pairs, version, + e::supported_schemas * updatable_snapshot_type_pairs, schema, snapshot_type_pair) { // Arrange BOOST_TEST_CHECKPOINT( - "(" << version << ", " << snapshot_type_pair + "(" << schema << ", " << snapshot_type_pair << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + auto db = e::create_temporary_database(schema); BOOST_TEST_CHECKPOINT( - "(" << version << ", " << snapshot_type_pair.initial << ", " + "(" << schema << ", " << snapshot_type_pair.initial << ", " << snapshot_type_pair.updated << ") Creating track..."); djinterop::track_snapshot initial{}; populate_track_snapshot( initial, snapshot_type_pair.initial, example_track_data_usage::create, - version); + schema); auto track = db.create_track(initial); djinterop::track_snapshot modified{}; populate_track_snapshot( modified, snapshot_type_pair.updated, example_track_data_usage::update, - version); + schema); // Act BOOST_TEST_CHECKPOINT( - "(" << version << ", " << snapshot_type_pair.initial << ", " + "(" << schema << ", " << snapshot_type_pair.initial << ", " << snapshot_type_pair.updated << ") Updating track..."); track.update(modified); @@ -202,7 +201,7 @@ BOOST_DATA_TEST_CASE( djinterop::track_snapshot expected{}; populate_track_snapshot( expected, snapshot_type_pair.updated, example_track_data_usage::fetch, - version); + schema); auto actual = track.snapshot(); BOOST_CHECK_EQUAL(expected, actual); } @@ -213,23 +212,23 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("set zero average loudness, all schema versions")) BOOST_DATA_TEST_CASE( - set_average_loudness__zero__no_loudness, e::all_versions, version) + set_average_loudness__zero__no_loudness, e::supported_schemas, schema) { // Arrange - BOOST_TEST_CHECKPOINT("(" << version << ") Creating temporary database..."); - auto db = e::create_temporary_database(version); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating temporary database..."); + auto db = e::create_temporary_database(schema); djinterop::track_snapshot snapshot{}; populate_track_snapshot( snapshot, example_track_data_variation::fully_analysed_1, - example_track_data_usage::create, version); + example_track_data_usage::create, schema); - BOOST_TEST_CHECKPOINT("(" << version << ") Creating track..."); + BOOST_TEST_CHECKPOINT("(" << schema << ") Creating track..."); auto track = db.create_track(snapshot); // Act BOOST_TEST_CHECKPOINT( - "(" << version << ") Setting zero average loudness..."); + "(" << schema << ") Setting zero average loudness..."); track.set_average_loudness(0); // Assert diff --git a/test/djinterop/engine/v2/example_track_row_data.hpp b/test/djinterop/engine/v2/example_track_row_data.hpp index 732cf78..184eeab 100644 --- a/test/djinterop/engine/v2/example_track_row_data.hpp +++ b/test/djinterop/engine/v2/example_track_row_data.hpp @@ -53,7 +53,7 @@ inline std::ostream& operator<<(std::ostream& o, example_track_row_type v) inline void populate_track_row( example_track_row_type row_type, djinterop::engine::v2::track_row& r, - const djinterop::engine::engine_version& version) + const djinterop::engine::engine_schema& schema) { using namespace std::string_literals; namespace ev = djinterop::engine; @@ -114,7 +114,8 @@ inline void populate_track_row( r.streaming_flags = 0; r.explicit_lyrics = false; - if (version.schema_version >= djinterop::semantic_version{2, 20, 1}) + if (schema >= + djinterop::engine::engine_schema::schema_2_20_1) r.active_on_load_loops = 123; else r.active_on_load_loops = std::nullopt; @@ -180,7 +181,8 @@ inline void populate_track_row( r.streaming_flags = 0; r.explicit_lyrics = false; - if (version.schema_version >= djinterop::semantic_version{2, 20, 1}) + if (schema >= + djinterop::engine::engine_schema::schema_2_20_1) r.active_on_load_loops = 123; else r.active_on_load_loops = std::nullopt; diff --git a/test/djinterop/engine/v2/playlist_entity_table_test.cpp b/test/djinterop/engine/v2/playlist_entity_table_test.cpp index c2673c4..6e7669a 100644 --- a/test/djinterop/engine/v2/playlist_entity_table_test.cpp +++ b/test/djinterop/engine/v2/playlist_entity_table_test.cpp @@ -3,7 +3,7 @@ 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 + the Free Software Foundation, either schema 3 of the License, or (at your option) any later version. libdjinterop is distributed in the hope that it will be useful, @@ -39,10 +39,10 @@ const constexpr int64_t EXAMPLE_TRACK_ID_2 = 67890; BOOST_TEST_DECORATOR(*utf::description("add_back() to empty playlist")) BOOST_DATA_TEST_CASE( - add_back__empty_playlist__adds, e::all_v2_versions, version) + add_back__empty_playlist__adds, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto playlist_entity_tbl = library.playlist_entity(); @@ -73,10 +73,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(*utf::description("add_back() to non-empty playlist")) BOOST_DATA_TEST_CASE( - add_back__nonempty_playlist__adds, e::all_v2_versions, version) + add_back__nonempty_playlist__adds, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto playlist_entity_tbl = library.playlist_entity(); @@ -102,7 +102,8 @@ BOOST_DATA_TEST_CASE( BOOST_CHECK_EQUAL(iter->track_id, pe_row_1.track_id); BOOST_CHECK_EQUAL(iter->database_uuid, db_uuid); BOOST_CHECK_EQUAL(iter->next_entity_id, pe_id_2); - BOOST_CHECK_EQUAL(iter->membership_reference, pe_row_1.membership_reference); + BOOST_CHECK_EQUAL( + iter->membership_reference, pe_row_1.membership_reference); ++iter; BOOST_REQUIRE(iter != entities.end()); BOOST_CHECK_EQUAL(iter->id, pe_id_2); @@ -111,7 +112,8 @@ BOOST_DATA_TEST_CASE( BOOST_CHECK_EQUAL(iter->database_uuid, db_uuid); BOOST_CHECK_EQUAL( iter->next_entity_id, ev2::PLAYLIST_ENTITY_NO_NEXT_ENTITY_ID); - BOOST_CHECK_EQUAL(iter->membership_reference, pe_row_2.membership_reference); + BOOST_CHECK_EQUAL( + iter->membership_reference, pe_row_2.membership_reference); ++iter; BOOST_REQUIRE(iter == entities.end()); } @@ -119,10 +121,11 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("add_back() same track multiple times idempotently")) BOOST_DATA_TEST_CASE( - add_back__same_track_multple_no_throw__idempotent, e::all_v2_versions, version) + add_back__same_track_multple_no_throw__idempotent, e::supported_v2_schemas, + schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto playlist_entity_tbl = library.playlist_entity(); @@ -155,10 +158,11 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("add_back() same track multiple times with throw")) BOOST_DATA_TEST_CASE( - add_back__same_track_multple_throw__idempotent, e::all_v2_versions, version) + add_back__same_track_multple_throw__idempotent, e::supported_v2_schemas, + schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto playlist_entity_tbl = library.playlist_entity(); diff --git a/test/djinterop/engine/v2/playlist_table_test.cpp b/test/djinterop/engine/v2/playlist_table_test.cpp index d6afe34..7a28cb8 100644 --- a/test/djinterop/engine/v2/playlist_table_test.cpp +++ b/test/djinterop/engine/v2/playlist_table_test.cpp @@ -3,7 +3,7 @@ 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 + the Free Software Foundation, either schema 3 of the License, or (at your option) any later version. libdjinterop is distributed in the hope that it will be useful, @@ -63,10 +63,10 @@ ev2::playlist_row make_playlist_row( } // anonymous namespace BOOST_TEST_DECORATOR(*utf::description("add() root playlist to empty database")) -BOOST_DATA_TEST_CASE(add__empty_root__adds, e::all_v2_versions, version) +BOOST_DATA_TEST_CASE(add__empty_root__adds, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto row = make_playlist_row("Example"); @@ -83,10 +83,10 @@ BOOST_DATA_TEST_CASE(add__empty_root__adds, e::all_v2_versions, version) BOOST_TEST_DECORATOR( *utf::description("add() root playlist to non-empty database")) -BOOST_DATA_TEST_CASE(add__nonempty_root__adds, e::all_v2_versions, version) +BOOST_DATA_TEST_CASE(add__nonempty_root__adds, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto prior_row = make_playlist_row("Example (Prior)"); @@ -106,10 +106,10 @@ BOOST_DATA_TEST_CASE(add__nonempty_root__adds, e::all_v2_versions, version) BOOST_TEST_DECORATOR( *utf::description("add() non-root playlist to empty database")) -BOOST_DATA_TEST_CASE(add__empty_nonroot__adds, e::all_v2_versions, version) +BOOST_DATA_TEST_CASE(add__empty_nonroot__adds, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto parent_row = make_playlist_row("Parent"); @@ -129,10 +129,10 @@ BOOST_DATA_TEST_CASE(add__empty_nonroot__adds, e::all_v2_versions, version) BOOST_TEST_DECORATOR( *utf::description("add() non-root playlist to non-empty database")) -BOOST_DATA_TEST_CASE(add__nonempty_nonroot__adds, e::all_v2_versions, version) +BOOST_DATA_TEST_CASE(add__nonempty_nonroot__adds, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto parent_row = make_playlist_row("Parent"); @@ -155,10 +155,10 @@ BOOST_DATA_TEST_CASE(add__nonempty_nonroot__adds, e::all_v2_versions, version) BOOST_TEST_DECORATOR(*utf::description("root_ids() after insertion at end")) BOOST_DATA_TEST_CASE( - root_ids__after_insert_end__ordered, e::all_v2_versions, version) + root_ids__after_insert_end__ordered, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto prior_row_1 = make_playlist_row("A"); @@ -186,10 +186,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("root_ids() after insertion at beginning")) BOOST_DATA_TEST_CASE( - root_ids__after_insert_beginning__ordered, e::all_v2_versions, version) + root_ids__after_insert_beginning__ordered, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto prior_row_1 = make_playlist_row("A"); @@ -216,10 +216,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(*utf::description("root_ids() after insertion at middle")) BOOST_DATA_TEST_CASE( - root_ids__after_insert_middle__ordered, e::all_v2_versions, version) + root_ids__after_insert_middle__ordered, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto prior_row_1 = make_playlist_row("A"); @@ -246,10 +246,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(*utf::description("child_ids() after insertion at end")) BOOST_DATA_TEST_CASE( - child_ids__after_insert_end__ordered, e::all_v2_versions, version) + child_ids__after_insert_end__ordered, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto parent_row = make_playlist_row("Parent"); @@ -279,10 +279,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("child_ids() after insertion at beginning")) BOOST_DATA_TEST_CASE( - child_ids__after_insert_beginning__ordered, e::all_v2_versions, version) + child_ids__after_insert_beginning__ordered, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto parent_row = make_playlist_row("Parent"); @@ -311,10 +311,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(*utf::description("child_ids() after insertion at middle")) BOOST_DATA_TEST_CASE( - child_ids__after_insert_middle__ordered, e::all_v2_versions, version) + child_ids__after_insert_middle__ordered, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto parent_row = make_playlist_row("Parent"); @@ -344,10 +344,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("update() from beginning to middle, same parent")) BOOST_DATA_TEST_CASE( - update__beginning_to_middle__ordered, e::all_v2_versions, version) + update__beginning_to_middle__ordered, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto row_1 = make_playlist_row("A"); @@ -376,10 +376,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("update() from end to middle, same parent")) BOOST_DATA_TEST_CASE( - update__end_to_middle__ordered, e::all_v2_versions, version) + update__end_to_middle__ordered, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto row_1 = make_playlist_row("A"); @@ -408,10 +408,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("update() from middle to beginning, same parent")) BOOST_DATA_TEST_CASE( - update__middle_to_beginning__ordered, e::all_v2_versions, version) + update__middle_to_beginning__ordered, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto row_1 = make_playlist_row("A"); @@ -440,10 +440,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("update() from middle to end, same parent")) BOOST_DATA_TEST_CASE( - update__middle_to_end__ordered, e::all_v2_versions, version) + update__middle_to_end__ordered, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto row_1 = make_playlist_row("A"); @@ -473,10 +473,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("update() from only child to root end")) BOOST_DATA_TEST_CASE( - update__only_child_to_root_end__moved, e::all_v2_versions, version) + update__only_child_to_root_end__moved, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto parent_row = make_playlist_row("Parent"); @@ -509,10 +509,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("update() from first child to root end")) BOOST_DATA_TEST_CASE( - update__first_child_to_root_end__moved, e::all_v2_versions, version) + update__first_child_to_root_end__moved, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto parent_row = make_playlist_row("Parent"); @@ -549,10 +549,10 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR( *utf::description("update() from last child to root beginning")) BOOST_DATA_TEST_CASE( - update__last_child_to_root_beginning__moved, e::all_v2_versions, version) + update__last_child_to_root_beginning__moved, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto db_uuid = library.information().get().uuid; auto playlist_tbl = library.playlist(); auto parent_row = make_playlist_row("Parent"); diff --git a/test/djinterop/engine/v2/track_table_test.cpp b/test/djinterop/engine/v2/track_table_test.cpp index 2bf65b2..a40e387 100644 --- a/test/djinterop/engine/v2/track_table_test.cpp +++ b/test/djinterop/engine/v2/track_table_test.cpp @@ -3,7 +3,7 @@ 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 + the Free Software Foundation, either schema 3 of the License, or (at your option) any later version. libdjinterop is distributed in the hope that it will be useful, @@ -23,6 +23,7 @@ #include #include +#include #include "../../boost_test_printable.hpp" #include "../../boost_test_utils.hpp" @@ -32,6 +33,7 @@ namespace utf = boost::unit_test; namespace e = djinterop::engine; namespace ev2 = djinterop::engine::v2; +using es = djinterop::engine::engine_schema; namespace { @@ -44,14 +46,14 @@ const std::vector all_example_track_row_types{ BOOST_TEST_DECORATOR(*utf::description("add() with valid track row")) BOOST_DATA_TEST_CASE( - add__valid__adds, e::all_v2_versions* all_example_track_row_types, version, - row_type) + add__valid__adds, e::supported_v2_schemas* all_example_track_row_types, + schema, row_type) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto track_tbl = library.track(); ev2::track_row row{0}; - populate_track_row(row_type, row, version); + populate_track_row(row_type, row, schema); // Act auto id = track_tbl.add(row); @@ -61,10 +63,10 @@ BOOST_DATA_TEST_CASE( } BOOST_TEST_DECORATOR(*utf::description("add() with an existing id")) -BOOST_DATA_TEST_CASE(add__existing_id__throws, e::all_v2_versions, version) +BOOST_DATA_TEST_CASE(add__existing_id__throws, e::supported_v2_schemas, schema) { // Arrange - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto track_tbl = library.track(); ev2::track_row row{123}; @@ -74,21 +76,21 @@ BOOST_DATA_TEST_CASE(add__existing_id__throws, e::all_v2_versions, version) BOOST_TEST_DECORATOR(*utf::description("get() with a valid id")) BOOST_DATA_TEST_CASE( - get__valid__gets, e::all_v2_versions* all_example_track_row_types, version, - row_type) + get__valid__gets, e::supported_v2_schemas* all_example_track_row_types, + schema, row_type) { // Arrange BOOST_TEST_CHECKPOINT( - "(" << version << ", " << row_type + "(" << schema << ", " << row_type << ") Creating temporary database..."); - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto track_tbl = library.track(); ev2::track_row expected{0}; - populate_track_row(row_type, expected, version); + populate_track_row(row_type, expected, schema); BOOST_TEST_CHECKPOINT( - "(" << version << ", " << row_type << ") Creating track..."); + "(" << schema << ", " << row_type << ") Creating track..."); auto id = track_tbl.add(expected); expected.id = id; @@ -101,7 +103,7 @@ BOOST_DATA_TEST_CASE( // Act BOOST_TEST_CHECKPOINT( - "(" << version << ", " << row_type << ") Fetching track..."); + "(" << schema << ", " << row_type << ") Fetching track..."); auto actual = track_tbl.get(id); // Assert @@ -113,31 +115,31 @@ BOOST_DATA_TEST_CASE( BOOST_TEST_DECORATOR(*utf::description("update() with valid data")) BOOST_DATA_TEST_CASE( update__valid__updates, - e::all_v2_versions* all_example_track_row_types* + e::supported_v2_schemas* all_example_track_row_types* all_example_track_row_types, - version, initial_row_type, update_row_type) + schema, initial_row_type, update_row_type) { // Arrange BOOST_TEST_CHECKPOINT( - "(" << version << ", " << initial_row_type << ", " << update_row_type + "(" << schema << ", " << initial_row_type << ", " << update_row_type << ") Creating temporary database..."); - auto library = ev2::engine_library::create_temporary(version); + auto library = ev2::engine_library::create_temporary(schema); auto track_tbl = library.track(); ev2::track_row initial{0}; - populate_track_row(initial_row_type, initial, version); + populate_track_row(initial_row_type, initial, schema); BOOST_TEST_CHECKPOINT( - "(" << version << ", " << initial_row_type << ", " << update_row_type + "(" << schema << ", " << initial_row_type << ", " << update_row_type << ") Creating initial track..."); auto id = track_tbl.add(initial); ev2::track_row expected{id}; - populate_track_row(update_row_type, expected, version); + populate_track_row(update_row_type, expected, schema); // Act BOOST_TEST_CHECKPOINT( - "(" << version << ", " << initial_row_type << ", " << update_row_type + "(" << schema << ", " << initial_row_type << ", " << update_row_type << ") Updating track..."); track_tbl.update(expected); @@ -150,7 +152,7 @@ BOOST_DATA_TEST_CASE( } BOOST_TEST_CHECKPOINT( - "(" << version << ", " << initial_row_type << ", " << update_row_type + "(" << schema << ", " << initial_row_type << ", " << update_row_type << ") Fetching track..."); auto actual = track_tbl.get(id); BOOST_REQUIRE(actual != std::nullopt); @@ -161,176 +163,174 @@ BOOST_DATA_TEST_CASE( // The act of defining very similar test cases for all the getters and setters // on the track table is highly tedious. As such, some macros to generate these // more efficiently make for a more succinct way to define tests. -#define DEFINE_GETTER_VALID_TEST_CASE(engine_column, min_schema_version) \ - BOOST_TEST_DECORATOR( \ - *utf::description("get_" #engine_column "() with valid track")) \ - BOOST_DATA_TEST_CASE( \ - get_##engine_column##__expected, e::all_v2_versions, version) \ - { \ - auto library = ev2::engine_library::create_temporary(version); \ - auto track_tbl = library.track(); \ - ev2::track_row row{0}; \ - populate_track_row( \ - example_track_row_type::fully_analysed_1, row, version); \ - auto id = track_tbl.add(row); \ - auto expected = row.engine_column; \ - \ - if (version.schema_version >= min_schema_version) \ - { \ - auto actual = track_tbl.get_##engine_column(id); \ - BOOST_CHECK_EQUAL( \ - make_printable(expected), make_printable(actual)); \ - } \ - else \ - { \ - BOOST_CHECK_THROW( \ - track_tbl.get_##engine_column(id), \ - djinterop::unsupported_operation); \ - } \ +#define DEFINE_GETTER_VALID_TEST_CASE(engine_column, min_schema) \ + BOOST_TEST_DECORATOR( \ + *utf::description("get_" #engine_column "() with valid track")) \ + BOOST_DATA_TEST_CASE( \ + get_##engine_column##__expected, e::supported_v2_schemas, schema) \ + { \ + auto library = ev2::engine_library::create_temporary(schema); \ + auto track_tbl = library.track(); \ + ev2::track_row row{0}; \ + populate_track_row( \ + example_track_row_type::fully_analysed_1, row, schema); \ + auto id = track_tbl.add(row); \ + auto expected = row.engine_column; \ + \ + if (schema >= min_schema) \ + { \ + auto actual = track_tbl.get_##engine_column(id); \ + BOOST_CHECK_EQUAL( \ + make_printable(expected), make_printable(actual)); \ + } \ + else \ + { \ + BOOST_CHECK_THROW( \ + track_tbl.get_##engine_column(id), \ + djinterop::unsupported_operation); \ + } \ } -#define DEFINE_SETTER_VALID_TEST_CASE(engine_column, min_schema_version) \ - BOOST_TEST_DECORATOR( \ - *utf::description("set_" #engine_column "() with valid track")) \ - BOOST_DATA_TEST_CASE( \ - set_##engine_column##__valid__expected, e::all_v2_versions, version) \ - { \ - auto library = ev2::engine_library::create_temporary(version); \ - auto track_tbl = library.track(); \ - ev2::track_row row{0}; \ - populate_track_row(example_track_row_type::minimal_1, row, version); \ - auto id = track_tbl.add(row); \ - \ - populate_track_row( \ - example_track_row_type::fully_analysed_1, row, version); \ - auto expected = row.engine_column; \ - \ - if (version.schema_version >= min_schema_version) \ - { \ - track_tbl.set_##engine_column(id, expected); \ - \ - auto actual = track_tbl.get_##engine_column(id); \ - BOOST_CHECK_EQUAL( \ - make_printable(expected), make_printable(actual)); \ - } \ - else \ - { \ - BOOST_CHECK_THROW( \ - track_tbl.set_##engine_column(id, expected), \ - djinterop::unsupported_operation); \ - } \ +#define DEFINE_SETTER_VALID_TEST_CASE(engine_column, min_schema) \ + BOOST_TEST_DECORATOR( \ + *utf::description("set_" #engine_column "() with valid track")) \ + BOOST_DATA_TEST_CASE( \ + set_##engine_column##__valid__expected, e::supported_v2_schemas, \ + schema) \ + { \ + auto library = ev2::engine_library::create_temporary(schema); \ + auto track_tbl = library.track(); \ + ev2::track_row row{0}; \ + populate_track_row(example_track_row_type::minimal_1, row, schema); \ + auto id = track_tbl.add(row); \ + \ + populate_track_row( \ + example_track_row_type::fully_analysed_1, row, schema); \ + auto expected = row.engine_column; \ + \ + if (schema >= min_schema) \ + { \ + track_tbl.set_##engine_column(id, expected); \ + \ + auto actual = track_tbl.get_##engine_column(id); \ + BOOST_CHECK_EQUAL( \ + make_printable(expected), make_printable(actual)); \ + } \ + else \ + { \ + BOOST_CHECK_THROW( \ + track_tbl.set_##engine_column(id, expected), \ + djinterop::unsupported_operation); \ + } \ } -#define DEFINE_GETTER_INVALID_TEST_CASE(engine_column, min_schema_version) \ - BOOST_TEST_DECORATOR( \ - *utf::description("get_" #engine_column "() with invalid track")) \ - BOOST_DATA_TEST_CASE( \ - get_##engine_column##__invalid__throws, e::all_v2_versions, version) \ - { \ - auto library = ev2::engine_library::create_temporary(version); \ - auto track_tbl = library.track(); \ - \ - if (version.schema_version >= min_schema_version) \ - { \ - BOOST_CHECK_THROW( \ - track_tbl.get_##engine_column(12345), \ - ev2::track_row_id_error); \ - } \ - else \ - { \ - BOOST_CHECK_THROW( \ - track_tbl.get_##engine_column(12345), \ - djinterop::unsupported_operation); \ - } \ +#define DEFINE_GETTER_INVALID_TEST_CASE(engine_column, min_schema) \ + BOOST_TEST_DECORATOR( \ + *utf::description("get_" #engine_column "() with invalid track")) \ + BOOST_DATA_TEST_CASE( \ + get_##engine_column##__invalid__throws, e::supported_v2_schemas, \ + schema) \ + { \ + auto library = ev2::engine_library::create_temporary(schema); \ + auto track_tbl = library.track(); \ + \ + if (schema >= min_schema) \ + { \ + BOOST_CHECK_THROW( \ + track_tbl.get_##engine_column(12345), \ + ev2::track_row_id_error); \ + } \ + else \ + { \ + BOOST_CHECK_THROW( \ + track_tbl.get_##engine_column(12345), \ + djinterop::unsupported_operation); \ + } \ } -#define DEFINE_SETTER_INVALID_TEST_CASE(engine_column, min_schema_version) \ - BOOST_TEST_DECORATOR( \ - *utf::description("set_" #engine_column "() with invalid track")) \ - BOOST_DATA_TEST_CASE( \ - set_##engine_column##__invalid__throws, e::all_v2_versions, version) \ - { \ - auto library = ev2::engine_library::create_temporary(version); \ - auto track_tbl = library.track(); \ - ev2::track_row row{0}; \ - populate_track_row( \ - example_track_row_type::fully_analysed_1, row, version); \ - \ - if (version.schema_version >= min_schema_version) \ - { \ - BOOST_CHECK_THROW( \ - track_tbl.set_##engine_column(12345, row.engine_column), \ - ev2::track_row_id_error); \ - } \ - else \ - { \ - BOOST_CHECK_THROW( \ - track_tbl.set_##engine_column(12345, row.engine_column), \ - djinterop::unsupported_operation); \ - } \ +#define DEFINE_SETTER_INVALID_TEST_CASE(engine_column, min_schema) \ + BOOST_TEST_DECORATOR( \ + *utf::description("set_" #engine_column "() with invalid track")) \ + BOOST_DATA_TEST_CASE( \ + set_##engine_column##__invalid__throws, e::supported_v2_schemas, \ + schema) \ + { \ + auto library = ev2::engine_library::create_temporary(schema); \ + auto track_tbl = library.track(); \ + ev2::track_row row{0}; \ + populate_track_row( \ + example_track_row_type::fully_analysed_1, row, schema); \ + \ + if (schema >= min_schema) \ + { \ + BOOST_CHECK_THROW( \ + track_tbl.set_##engine_column(12345, row.engine_column), \ + ev2::track_row_id_error); \ + } \ + else \ + { \ + BOOST_CHECK_THROW( \ + track_tbl.set_##engine_column(12345, row.engine_column), \ + djinterop::unsupported_operation); \ + } \ } -#define DEFINE_GETTER_SETTER_TEST_CASES(engine_column, min_schema_version) \ - DEFINE_GETTER_VALID_TEST_CASE(engine_column, min_schema_version) \ - DEFINE_GETTER_INVALID_TEST_CASE(engine_column, min_schema_version) \ - DEFINE_SETTER_VALID_TEST_CASE(engine_column, min_schema_version) \ - DEFINE_SETTER_INVALID_TEST_CASE(engine_column, min_schema_version) - -DEFINE_GETTER_SETTER_TEST_CASES(play_order, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(length, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(bpm, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(year, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(path, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(filename, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(bitrate, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(bpm_analyzed, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(album_art_id, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(file_bytes, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(title, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(artist, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(album, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(genre, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(comment, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(label, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(composer, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(remixer, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(key, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(rating, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(album_art, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(time_last_played, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(is_played, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(file_type, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(is_analyzed, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(date_created, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(date_added, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(is_available, e::os_2_0_0.schema_version) +#define DEFINE_GETTER_SETTER_TEST_CASES(engine_column, min_schema) \ + DEFINE_GETTER_VALID_TEST_CASE(engine_column, min_schema) \ + DEFINE_GETTER_INVALID_TEST_CASE(engine_column, min_schema) \ + DEFINE_SETTER_VALID_TEST_CASE(engine_column, min_schema) \ + DEFINE_SETTER_INVALID_TEST_CASE(engine_column, min_schema) + +DEFINE_GETTER_SETTER_TEST_CASES(play_order, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(length, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(bpm, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(year, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(path, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(filename, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(bitrate, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(bpm_analyzed, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(album_art_id, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(file_bytes, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(title, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(artist, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(album, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(genre, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(comment, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(label, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(composer, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(remixer, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(key, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(rating, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(album_art, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(time_last_played, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(is_played, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(file_type, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(is_analyzed, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(date_created, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(date_added, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(is_available, es::schema_2_18_0) DEFINE_GETTER_SETTER_TEST_CASES( - is_metadata_of_packed_track_changed, e::os_2_0_0.schema_version) + is_metadata_of_packed_track_changed, es::schema_2_18_0) DEFINE_GETTER_SETTER_TEST_CASES( - is_performance_data_of_packed_track_changed, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(played_indicator, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES( - is_metadata_imported, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(pdb_import_key, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(streaming_source, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(uri, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(is_beat_grid_locked, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES( - origin_database_uuid, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(origin_track_id, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(track_data, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES( - overview_waveform_data, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(beat_data, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(quick_cues, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(loops, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES( - third_party_source_id, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(streaming_flags, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(explicit_lyrics, e::os_2_0_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES( - active_on_load_loops, e::os_2_2_0.schema_version) -DEFINE_GETTER_SETTER_TEST_CASES(last_edit_time, e::os_3_0_0.schema_version) + is_performance_data_of_packed_track_changed, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(played_indicator, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(is_metadata_imported, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(pdb_import_key, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(streaming_source, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(uri, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(is_beat_grid_locked, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(origin_database_uuid, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(origin_track_id, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(track_data, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(overview_waveform_data, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(beat_data, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(quick_cues, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(loops, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(third_party_source_id, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(streaming_flags, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(explicit_lyrics, es::schema_2_18_0) +DEFINE_GETTER_SETTER_TEST_CASES(active_on_load_loops, es::schema_2_20_1) +DEFINE_GETTER_SETTER_TEST_CASES(last_edit_time, es::schema_2_20_3) #undef DEFINE_GETTER_SETTER_TEST_CASES #undef DEFINE_SETTER_INVALID_TEST_CASE @@ -341,11 +341,11 @@ DEFINE_GETTER_SETTER_TEST_CASES(last_edit_time, e::os_3_0_0.schema_version) BOOST_TEST_DECORATOR(*utf::description("operator<<() with valid track row")) BOOST_DATA_TEST_CASE( operator_stream_output__valid__outputs, - e::all_v2_versions* all_example_track_row_types, version, row_type) + e::supported_v2_schemas* all_example_track_row_types, schema, row_type) { // Arrange ev2::track_row row{0}; - populate_track_row(row_type, row, version); + populate_track_row(row_type, row, schema); std::stringstream ss; // Act diff --git a/testdata/ref/engine/desktop/desktop-4.1.0/Database2/m.db.sql b/testdata/ref/engine/desktop/desktop-4.1.0/Database2/m.db.sql new file mode 100644 index 0000000..c6ea534 --- /dev/null +++ b/testdata/ref/engine/desktop/desktop-4.1.0/Database2/m.db.sql @@ -0,0 +1,50 @@ +PRAGMA foreign_keys=OFF; +BEGIN TRANSACTION; +CREATE TABLE Information ( id INTEGER PRIMARY KEY AUTOINCREMENT, uuid TEXT, schemaVersionMajor INTEGER, schemaVersionMinor INTEGER, schemaVersionPatch INTEGER, currentPlayedIndiciator INTEGER, lastRekordBoxLibraryImportReadCounter INTEGER); +INSERT INTO Information VALUES(1,'43b432fc-8c09-40cd-8ef4-9fcb9a853f84',3,0,0,8054931482920806910,NULL); +CREATE TABLE AlbumArt ( id INTEGER PRIMARY KEY AUTOINCREMENT, hash TEXT, albumArt BLOB ); +CREATE TABLE Pack ( id INTEGER PRIMARY KEY AUTOINCREMENT, packId TEXT, changeLogDatabaseUuid TEXT, changeLogId INTEGER, lastPackTime DATETIME ); +CREATE TABLE Playlist ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, parentListId INTEGER, isPersisted BOOLEAN, nextListId INTEGER, lastEditTime DATETIME, isExplicitlyExported BOOLEAN, CONSTRAINT C_NAME_UNIQUE_FOR_PARENT UNIQUE (title, parentListId), CONSTRAINT C_NEXT_LIST_ID_UNIQUE_FOR_PARENT UNIQUE (parentListId, nextListId) ); +CREATE TABLE PlaylistEntity ( id INTEGER PRIMARY KEY AUTOINCREMENT, listId INTEGER, trackId INTEGER, databaseUuid TEXT, nextEntityId INTEGER, membershipReference INTEGER, CONSTRAINT C_NAME_UNIQUE_FOR_LIST UNIQUE (listId, databaseUuid, trackId), FOREIGN KEY (listId) REFERENCES Playlist (id) ON DELETE CASCADE ); +CREATE TABLE Smartlist ( listUuid TEXT NOT NULL PRIMARY KEY, title TEXT, parentPlaylistPath TEXT, nextPlaylistPath TEXT, nextListUuid TEXT, rules TEXT, lastEditTime DATETIME, CONSTRAINT C_NAME_UNIQUE_FOR_PARENT UNIQUE (title, parentPlaylistPath), CONSTRAINT C_NEXT_LIST_UNIQUE_FOR_PARENT UNIQUE (parentPlaylistPath, nextPlaylistPath, nextListUuid) ); +CREATE TABLE Track ( id INTEGER PRIMARY KEY AUTOINCREMENT, playOrder INTEGER, length INTEGER, bpm INTEGER, year INTEGER, path TEXT, filename TEXT, bitrate INTEGER, bpmAnalyzed REAL, albumArtId INTEGER, fileBytes INTEGER, title TEXT, artist TEXT, album TEXT, genre TEXT, comment TEXT, label TEXT, composer TEXT, remixer TEXT, key INTEGER, rating INTEGER, albumArt TEXT, timeLastPlayed DATETIME, isPlayed BOOLEAN, fileType TEXT, isAnalyzed BOOLEAN, dateCreated DATETIME, dateAdded DATETIME, isAvailable BOOLEAN, isMetadataOfPackedTrackChanged BOOLEAN, isPerfomanceDataOfPackedTrackChanged BOOLEAN, playedIndicator INTEGER, isMetadataImported BOOLEAN, pdbImportKey INTEGER, streamingSource TEXT, uri TEXT, isBeatGridLocked BOOLEAN, originDatabaseUuid TEXT, originTrackId INTEGER, streamingFlags INTEGER, explicitLyrics BOOLEAN, lastEditTime DATETIME, CONSTRAINT C_originDatabaseUuid_originTrackId UNIQUE (originDatabaseUuid, originTrackId), CONSTRAINT C_path UNIQUE (path), FOREIGN KEY (albumArtId) REFERENCES AlbumArt (id) ON DELETE RESTRICT ); +CREATE TABLE PerformanceData ( trackId INTEGER PRIMARY KEY, trackData BLOB, overviewWaveFormData BLOB, beatData BLOB, quickCues BLOB, loops BLOB, thirdPartySourceId INTEGER, activeOnLoadLoops INTEGER, FOREIGN KEY(trackId) REFERENCES Track(id) ON DELETE CASCADE ON UPDATE CASCADE ); +CREATE TABLE PreparelistEntity ( id INTEGER PRIMARY KEY AUTOINCREMENT, trackId INTEGER, trackNumber INTEGER, FOREIGN KEY (trackId) REFERENCES Track (id) ON DELETE CASCADE ); +DELETE FROM sqlite_sequence; +INSERT INTO sqlite_sequence VALUES('Information',1); +CREATE INDEX index_AlbumArt_hash ON AlbumArt (hash); +CREATE INDEX index_PlaylistEntity_nextEntityId_listId ON PlaylistEntity(nextEntityId, listId); +CREATE TRIGGER trigger_after_insert_Pack_timestamp AFTER INSERT ON Pack FOR EACH ROW WHEN NEW.lastPackTime IS NULL BEGIN UPDATE Pack SET lastPackTime = strftime('%s') WHERE ROWID = NEW.ROWID; END; +CREATE TRIGGER trigger_after_insert_Pack_changeLogId AFTER INSERT ON Pack FOR EACH ROW WHEN NEW.changeLogId = 0 BEGIN UPDATE Pack SET changeLogId = 1 WHERE ROWID = NEW.ROWID; END; +CREATE VIEW ChangeLog (id, trackId) AS SELECT 0, 0 WHERE FALSE; +CREATE TRIGGER trigger_before_insert_List BEFORE INSERT ON Playlist FOR EACH ROW BEGIN UPDATE Playlist SET nextListId = -(1 + nextListId) WHERE nextListId = NEW.nextListId AND parentListId = NEW.parentListId; END; +CREATE TRIGGER trigger_after_insert_List AFTER INSERT ON Playlist FOR EACH ROW BEGIN UPDATE Playlist SET nextListId = NEW.id WHERE nextListId = -(1 + NEW.nextListId) AND parentListId = NEW.parentListId; END; +CREATE TRIGGER trigger_after_delete_List AFTER DELETE ON Playlist FOR EACH ROW BEGIN UPDATE Playlist SET nextListId = OLD.nextListId WHERE nextListId = OLD.id; DELETE FROM Playlist WHERE parentListId = OLD.id; END; +CREATE TRIGGER trigger_after_update_isPersistParent AFTER UPDATE ON Playlist WHEN (old.isPersisted = 0 AND new.isPersisted = 1) OR (old.parentListId != new.parentListId AND new.isPersisted = 1) BEGIN UPDATE Playlist SET isPersisted = 1 WHERE id IN (SELECT parentListId FROM PlaylistAllParent WHERE id=new.id); END; +CREATE TRIGGER trigger_after_update_isPersistChild AFTER UPDATE ON Playlist WHEN old.isPersisted = 1 AND new.isPersisted = 0 BEGIN UPDATE Playlist SET isPersisted = 0 WHERE id IN (SELECT childListId FROM PlaylistAllChildren WHERE id=new.id); END; +CREATE TRIGGER trigger_after_insert_isPersist AFTER INSERT ON Playlist WHEN new.isPersisted = 1 BEGIN UPDATE Playlist SET isPersisted = 1 WHERE id IN (SELECT parentListId FROM PlaylistAllParent WHERE id=new.id); END; +CREATE VIEW PlaylistAllParent AS WITH FindAllParent AS ( SELECT id, parentListId FROM Playlist UNION ALL SELECT recursiveCTE.id, Plist.parentListId FROM Playlist Plist INNER JOIN FindAllParent recursiveCTE ON recursiveCTE.parentListId = Plist.id ) SELECT * FROM FindAllParent; +CREATE VIEW PlaylistAllChildren AS WITH FindAllChild AS ( SELECT id, id as childListId FROM Playlist UNION ALL SELECT recursiveCTE.id, Plist.id FROM Playlist Plist INNER JOIN FindAllChild recursiveCTE ON recursiveCTE.childListId = Plist.parentListId ) SELECT * FROM FindAllChild WHERE id <> childListId; +CREATE VIEW PlaylistPath AS WITH RECURSIVE Heirarchy AS ( SELECT id AS child, parentListId AS parent, title AS name, 1 AS depth FROM Playlist UNION ALL SELECT child, parentListId AS parent, title AS name, h.depth + 1 AS depth FROM Playlist c JOIN Heirarchy h ON h.parent = c.id ORDER BY depth DESC ), OrderedList AS ( SELECT id , nextListId, 1 AS position FROM Playlist WHERE nextListId = 0 UNION ALL SELECT c.id , c.nextListId , l.position + 1 FROM Playlist c INNER JOIN OrderedList l ON c.nextListId = l.id ), NameConcat AS ( SELECT child AS id, GROUP_CONCAT(name ,';') || ';' AS path FROM ( SELECT child, name FROM Heirarchy ORDER BY depth DESC ) GROUP BY child ) SELECT id, path, ROW_NUMBER() OVER ( ORDER BY (SELECT COUNT(*) FROM (SELECT * FROM Heirarchy WHERE child = id) ) DESC, (SELECT position FROM OrderedList ol WHERE ol.id = c.id) ASC ) AS position FROM Playlist c LEFT JOIN NameConcat g USING (id); +CREATE TRIGGER trigger_before_delete_PlaylistEntity BEFORE DELETE ON PlaylistEntity WHEN OLD.trackId > 0 BEGIN UPDATE PlaylistEntity SET nextEntityId = OLD.nextEntityId WHERE nextEntityId = OLD.id AND listId = OLD.listId; END; +CREATE INDEX index_Track_filename ON Track (filename); +CREATE INDEX index_Track_albumArtId ON Track (albumArtId); +CREATE INDEX index_Track_uri ON Track (uri); +CREATE INDEX index_Track_title ON Track(title); +CREATE INDEX index_Track_length ON Track(length); +CREATE INDEX index_Track_rating ON Track(rating); +CREATE INDEX index_Track_year ON Track(year); +CREATE INDEX index_Track_dateAdded ON Track(dateAdded); +CREATE INDEX index_Track_genre ON Track(genre); +CREATE INDEX index_Track_artist ON Track(artist); +CREATE INDEX index_Track_album ON Track(album); +CREATE INDEX index_Track_key ON Track(key); +CREATE INDEX index_Track_bpmAnalyzed ON Track(CAST(bpmAnalyzed + 0.5 AS int)); +CREATE TRIGGER trigger_after_insert_Track_check_id AFTER INSERT ON Track WHEN NEW.id <= (SELECT seq FROM sqlite_sequence WHERE name = 'Track') BEGIN SELECT RAISE(ABORT, 'Recycling deleted track id''s are not allowed'); END; +CREATE TRIGGER trigger_after_update_Track_check_Id BEFORE UPDATE ON Track WHEN NEW.id <> OLD.id BEGIN SELECT RAISE(ABORT, 'Changing track id''s are not allowed'); END; +CREATE TRIGGER trigger_after_insert_Track_fix_origin AFTER INSERT ON Track WHEN IFNULL(NEW.originTrackId, 0) = 0 OR IFNULL(NEW.originDatabaseUuid, '') = '' BEGIN UPDATE Track SET originTrackId = NEW.id, originDatabaseUuid = (SELECT uuid FROM Information) WHERE track.id = NEW.id; END; +CREATE TRIGGER trigger_after_update_Track_fix_origin AFTER UPDATE ON Track WHEN IFNULL(NEW.originTrackId, 0) = 0 OR IFNULL(NEW.originDatabaseUuid, '') = '' BEGIN UPDATE Track SET originTrackId = NEW.id, originDatabaseUuid = (SELECT uuid FROM Information) WHERE track.id = NEW.id; END; +CREATE TRIGGER trigger_after_update_Track_timestamp AFTER UPDATE OF length, bpm, year, filename, bitrate, bpmAnalyzed, albumArtId, title, artist, album, genre, comment, label, composer, remixer, key, rating, albumArt, fileType, isAnalyzed, isBeatgridLocked, trackData, overviewWaveformData, beatData, quickCues, loops, explicitLyrics, activeOnLoadLoops ON Track FOR EACH ROW BEGIN UPDATE Track SET lastEditTime = strftime('%s') WHERE ROWID=NEW.ROWID; END; +CREATE TRIGGER trigger_after_insert_Track_insert_performance_data AFTER INSERT ON Track BEGIN INSERT INTO PerformanceData(trackId) VALUES(NEW.id); END; +CREATE INDEX index_PreparelistEntity_trackId ON PreparelistEntity (trackId); +COMMIT;