From 045f83a2c29b50b6e845e4f3349c6e7b687fe8f0 Mon Sep 17 00:00:00 2001 From: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com> Date: Sun, 27 Sep 2026 23:58:55 -0700 Subject: [PATCH] fix: add 'IF NOT EXISTS' to migration 200 template tables --- .../core/library/alchemy/migrations.py | 6 ++-- tests/core/library/test_migrations.py | 34 ++++++++++++++++++ .../issue-1500/.TagStudio/ts_library.sqlite | Bin 0 -> 122880 bytes 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/issue-1500/.TagStudio/ts_library.sqlite 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 0000000000000000000000000000000000000000..74c1a3396438e25432b2c05af70420362f39c196 GIT binary patch literal 122880 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 literal 0 HcmV?d00001