diff --git a/src/tagstudio/core/library/alchemy/migrations.py b/src/tagstudio/core/library/alchemy/migrations.py index 69561a38e..54cdf6c1a 100644 --- a/src/tagstudio/core/library/alchemy/migrations.py +++ b/src/tagstudio/core/library/alchemy/migrations.py @@ -445,15 +445,17 @@ def run(cls, conn: Connection, library_dir: Path, fmt_log: LoggingMethod): """) # Add field templates tables + # NOTE: v9.6.0-9.6.2 created these empty tables before migrating, so backup library files + # or libraries that failed mid-migration may already have them. conn.execute(""" - CREATE TABLE text_field_templates ( + CREATE TABLE IF NOT EXISTS text_field_templates ( id INTEGER NOT NULL PRIMARY KEY, is_multiline BOOLEAN NOT NULL, name VARCHAR NOT NULL ) """) conn.execute(""" - CREATE TABLE datetime_field_templates ( + CREATE TABLE IF NOT EXISTS datetime_field_templates ( id INTEGER NOT NULL PRIMARY KEY, name VARCHAR NOT NULL ) diff --git a/tests/core/library/test_migrations.py b/tests/core/library/test_migrations.py index 4f6c77b9e..256806842 100644 --- a/tests/core/library/test_migrations.py +++ b/tests/core/library/test_migrations.py @@ -56,3 +56,37 @@ def test_library_migrations(path: str): raise (e) finally: shutil.rmtree(temp_path) + + +def test_migration_with_existing_field_template_tables(tmp_path: Path): + """DB_VERSION 104 library that already has the (empty) field template tables. + + v9.6.0-9.6.2 created these empty tables before migrating, so backup library files + or libraries that failed mid-migration may already have them. + + This tests for a specific scenario where a backup library file from a + v9.5.6 (DB102) -> v9.6.2 (DB300) migration is opened in a newer version. + Historically this would cause breakage in v9.6.3 (see issue #1500). + """ + fixture = CWD.parents[2] / FIXTURES / "issue-1500" / TS_FOLDER_NAME / SQL_FILENAME + (tmp_path / TS_FOLDER_NAME).mkdir() + shutil.copy(fixture, tmp_path / TS_FOLDER_NAME / SQL_FILENAME) + + library = Library() + try: + assert library.open_library(library_dir=tmp_path).success + expected = ["Title", "Author", "Artist", "URL", "Description", "Notes", "Comments", "Date"] + assert [t.name for t in library.field_templates] == expected + + assert library.entries_count == 2 + entry = library.get_entry_full(entry_id=2) + assert entry + assert {f.name: f.value for f in entry.text_fields} == { + "Title": "Mario", + "Description": "This is a cat.", + } + assert {f.name: f.value for f in entry.datetime_fields} == { + "Date": None, + } + finally: + library.close() diff --git a/tests/fixtures/issue-1500/.TagStudio/ts_library.sqlite b/tests/fixtures/issue-1500/.TagStudio/ts_library.sqlite new file mode 100644 index 000000000..74c1a3396 Binary files /dev/null and b/tests/fixtures/issue-1500/.TagStudio/ts_library.sqlite differ