From 6cb47981b94ee8951064b76d15bb56919b2a484d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=BCgler?= Date: Thu, 17 Sep 2026 21:31:39 +0200 Subject: [PATCH] Create the Engine 3.0.2 Information row at id 1 The schema creator for 3.0.2 seeds the table's AUTOINCREMENT counter before inserting into it: INSERT INTO sqlite_sequence VALUES('Information',1); INSERT INTO Information ([uuid], ...) VALUES (?, ...); With the counter already at 1, the row lands at id 2. Engine keeps exactly one row in that table and keeps it at id 1: a Denon-written stick has it at 1, and so does the database a Prime 4 writes for itself. A Prime 4 given a 3.0.2 library created by this library reports the stick as corrupt and replaces `m.db` with an empty database of its own, discarding every track. Renumbering the row to 1 and changing nothing else is enough to stop that. The `Information` table is also where the schema version lives, so it is the first thing anything reading the library looks at, `detect_schema()` included. Only 3.0.2 seeds the counter; no other schema version in that directory does, and libraries created at 1.x and 2.x already come out with the row at id 1. Dropping the statement leaves the same end state a real library has: the row at id 1, and `sqlite_sequence` reading 1 for the table, written by the insert itself. New tests assert the row's id for every 2.x and 3.x schema version, and the doc comment on `information_row::id` -- which called the value not especially meaningful -- now says what hardware expects of it. Co-authored-by: Claude Opus 5 --- CMakeLists.txt | 2 + .../djinterop/engine/v2/information_table.hpp | 3 +- src/djinterop/engine/schema/schema_3_0_2.cpp | 1 - .../engine/v2/information_table_test.cpp | 41 +++++++++++++++++++ .../engine/v3/information_table_test.cpp | 41 +++++++++++++++++++ 5 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 test/djinterop/engine/v2/information_table_test.cpp create mode 100644 test/djinterop/engine/v3/information_table_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 769abe9..ec84b98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -444,9 +444,11 @@ if (Boost_FOUND AND Boost_filesystem_FOUND AND Boost_system_FOUND) add_djinterop_test(engine/ engine_test) add_djinterop_test(engine/ playlist_test) add_djinterop_test(engine/ track_test) + add_djinterop_test(engine/v2/ information_table_test) add_djinterop_test(engine/v2/ playlist_entity_table_test) add_djinterop_test(engine/v2/ playlist_table_test) add_djinterop_test(engine/v2/ track_table_test) + add_djinterop_test(engine/v3/ information_table_test) add_djinterop_test(engine/v3/ performance_data_table_test) add_djinterop_test(engine/v3/ track_table_test) diff --git a/include/djinterop/engine/v2/information_table.hpp b/include/djinterop/engine/v2/information_table.hpp index 14643c0..ba363d3 100644 --- a/include/djinterop/engine/v2/information_table.hpp +++ b/include/djinterop/engine/v2/information_table.hpp @@ -36,7 +36,8 @@ struct DJINTEROP_PUBLIC information_row { /// Auto-generated id column. /// - /// Not especially meaningful when there is only a single row in the table. + /// The table holds only one row, and Engine keeps it at id 1. Hardware + /// players have been observed to reject a library whose row is elsewhere. int64_t id; /// UUID for the database. diff --git a/src/djinterop/engine/schema/schema_3_0_2.cpp b/src/djinterop/engine/schema/schema_3_0_2.cpp index db07400..06f7d4d 100644 --- a/src/djinterop/engine/schema/schema_3_0_2.cpp +++ b/src/djinterop/engine/schema/schema_3_0_2.cpp @@ -322,7 +322,6 @@ void schema_3_0_2::create(sqlite::database& db) "CONSTRAINT C_NAME_UNIQUE_FOR_PARENT UNIQUE (title, " "parentPlaylistPath), CONSTRAINT C_NEXT_LIST_UNIQUE_FOR_PARENT " "UNIQUE (parentPlaylistPath, nextPlaylistPath, nextListUuid) );"; - db << "INSERT INTO sqlite_sequence VALUES('Information',1);"; db << "CREATE VIEW ChangeLog (id, trackId) AS SELECT 0, 0 WHERE FALSE;"; db << "CREATE VIEW PlaylistAllParent AS WITH FindAllParent AS ( SELECT " "id, parentListId FROM Playlist UNION ALL SELECT " diff --git a/test/djinterop/engine/v2/information_table_test.cpp b/test/djinterop/engine/v2/information_table_test.cpp new file mode 100644 index 0000000..f957a15 --- /dev/null +++ b/test/djinterop/engine/v2/information_table_test.cpp @@ -0,0 +1,41 @@ +/* + 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 . + */ + +#define BOOST_TEST_MODULE engine_v2_information_table_test +#include +#include + +#include +#include + +namespace utf = boost::unit_test; +namespace e = djinterop::engine; +namespace ev2 = djinterop::engine::v2; + +BOOST_TEST_DECORATOR(*utf::description("get() on a newly-created library")) +BOOST_DATA_TEST_CASE( + get__new_library__row_at_id_one, e::supported_v2_schemas, schema) +{ + // Arrange + auto library = ev2::engine_library::create_temporary(schema); + + // Act + auto row = library.information().get(); + + // Assert + BOOST_CHECK_EQUAL(row.id, 1); +} diff --git a/test/djinterop/engine/v3/information_table_test.cpp b/test/djinterop/engine/v3/information_table_test.cpp new file mode 100644 index 0000000..d3a252b --- /dev/null +++ b/test/djinterop/engine/v3/information_table_test.cpp @@ -0,0 +1,41 @@ +/* + 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 . + */ + +#define BOOST_TEST_MODULE engine_v3_information_table_test +#include +#include + +#include +#include + +namespace utf = boost::unit_test; +namespace e = djinterop::engine; +namespace ev3 = djinterop::engine::v3; + +BOOST_TEST_DECORATOR(*utf::description("get() on a newly-created library")) +BOOST_DATA_TEST_CASE( + get__new_library__row_at_id_one, e::supported_v3_schemas, schema) +{ + // Arrange + auto library = ev3::engine_library::create_temporary(schema); + + // Act + auto row = library.information().get(); + + // Assert + BOOST_CHECK_EQUAL(row.id, 1); +}