From 32d1f07c4b9a509d885ec87fe43c7ca3a745e0bb Mon Sep 17 00:00:00 2001 From: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com> Date: Sun, 2 Feb 2025 18:17:28 -0800 Subject: [PATCH 1/2] fix: update DESCRIPTION to the TEXT_BOX type --- tagstudio/src/core/library/alchemy/fields.py | 2 +- tagstudio/src/core/library/alchemy/library.py | 35 ++++++++++++++++--- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/tagstudio/src/core/library/alchemy/fields.py b/tagstudio/src/core/library/alchemy/fields.py index 4f0b33b32..2c9ab41a1 100644 --- a/tagstudio/src/core/library/alchemy/fields.py +++ b/tagstudio/src/core/library/alchemy/fields.py @@ -113,7 +113,7 @@ class _FieldID(Enum): AUTHOR = DefaultField(id=1, name="Author", type=FieldTypeEnum.TEXT_LINE) ARTIST = DefaultField(id=2, name="Artist", type=FieldTypeEnum.TEXT_LINE) URL = DefaultField(id=3, name="URL", type=FieldTypeEnum.TEXT_LINE) - DESCRIPTION = DefaultField(id=4, name="Description", type=FieldTypeEnum.TEXT_LINE) + DESCRIPTION = DefaultField(id=4, name="Description", type=FieldTypeEnum.TEXT_BOX) NOTES = DefaultField(id=5, name="Notes", type=FieldTypeEnum.TEXT_BOX) COLLATION = DefaultField(id=9, name="Collation", type=FieldTypeEnum.TEXT_LINE) DATE = DefaultField(id=10, name="Date", type=FieldTypeEnum.DATETIME) diff --git a/tagstudio/src/core/library/alchemy/library.py b/tagstudio/src/core/library/alchemy/library.py index 83466280e..106896aca 100644 --- a/tagstudio/src/core/library/alchemy/library.py +++ b/tagstudio/src/core/library/alchemy/library.py @@ -318,6 +318,7 @@ def open_sqlite_library(self, library_dir: Path, is_new: bool) -> LibraryStatus: # https://docs.sqlalchemy.org/en/20/changelog/migration_07.html # Under -> sqlite-the-sqlite-dialect-now-uses-nullpool-for-file-based-databases poolclass = None if self.storage_path == ":memory:" else NullPool + db_version: int = 0 logger.info( "[Library] Opening SQLite Library", @@ -328,11 +329,13 @@ def open_sqlite_library(self, library_dir: Path, is_new: bool) -> LibraryStatus: with Session(self.engine) as session: # dont check db version when creating new library if not is_new: - db_version = session.scalar( + db_result = session.scalar( select(Preferences).where(Preferences.key == LibraryPrefs.DB_VERSION.name) ) + if db_result: + db_version = db_result.value # type: ignore - if not db_version or db_version.value != LibraryPrefs.DB_VERSION.default: + if db_version != LibraryPrefs.DB_VERSION.default: mismatch_text = Translations.translate_formatted( "status.library_version_mismatch" ) @@ -344,15 +347,14 @@ def open_sqlite_library(self, library_dir: Path, is_new: bool) -> LibraryStatus: success=False, message=( f"{mismatch_text}\n" - f"{found_text} v{0 if not db_version else db_version.value}, " + f"{found_text} v{db_version}, " f"{expected_text} v{LibraryPrefs.DB_VERSION.default}" ), ) + logger.info(f"[Library] DB_VERSION: {db_version}") make_tables(self.engine) - # TODO: Determine a good way of updating built-in data after updates. - # Add default tag color namespaces. if is_new: namespaces = default_color_groups.namespaces() @@ -425,10 +427,33 @@ def open_sqlite_library(self, library_dir: Path, is_new: bool) -> LibraryStatus: session.commit() self.folder = folder + # Apply any post-SQL migration patches. + if not is_new: + # NOTE: DB_VERSION 6 was first used in v9.5.0-pr1 + if db_version >= 6: + self.apply_db6_patches(session) + else: + pass + # everything is fine, set the library path self.library_dir = library_dir return LibraryStatus(success=True, library_path=library_dir) + def apply_db6_patches(self, session: Session): + """Apply migration patches to a library with DB_VERSION 6. + + DB_VERSION 6 was first used in v9.5.0-pr1. + """ + with session: + # Repair "Description" fields with a TEXT_LINE key instead of a TEXT_BOX key + statement = ( + update(ValueType) + .where(ValueType.key == _FieldID.DESCRIPTION.name) + .values(type=FieldTypeEnum.TEXT_BOX.name) + ) + session.execute(statement) + session.commit() + @property def default_fields(self) -> list[BaseField]: with Session(self.engine) as session: From b9a70f7a248b214fcfd1057cb8e5b02889d95695 Mon Sep 17 00:00:00 2001 From: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com> Date: Sun, 2 Feb 2025 18:39:16 -0800 Subject: [PATCH 2/2] fix: remove `disambiguation_id` refs with a tag is deleted --- tagstudio/src/core/enums.py | 2 +- tagstudio/src/core/library/alchemy/library.py | 35 +++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/tagstudio/src/core/enums.py b/tagstudio/src/core/enums.py index 909599bb4..6cdedbcfb 100644 --- a/tagstudio/src/core/enums.py +++ b/tagstudio/src/core/enums.py @@ -71,4 +71,4 @@ class LibraryPrefs(DefaultEnum): IS_EXCLUDE_LIST = True EXTENSION_LIST: list[str] = [".json", ".xmp", ".aae"] PAGE_SIZE: int = 500 - DB_VERSION: int = 6 + DB_VERSION: int = 7 diff --git a/tagstudio/src/core/library/alchemy/library.py b/tagstudio/src/core/library/alchemy/library.py index 106896aca..d667ac0fd 100644 --- a/tagstudio/src/core/library/alchemy/library.py +++ b/tagstudio/src/core/library/alchemy/library.py @@ -335,7 +335,7 @@ def open_sqlite_library(self, library_dir: Path, is_new: bool) -> LibraryStatus: if db_result: db_version = db_result.value # type: ignore - if db_version != LibraryPrefs.DB_VERSION.default: + if db_version < 6: # NOTE: DB_VERSION 6 is the first supported SQL DB version. mismatch_text = Translations.translate_formatted( "status.library_version_mismatch" ) @@ -423,18 +423,20 @@ def open_sqlite_library(self, library_dir: Path, is_new: bool) -> LibraryStatus: ) session.add(folder) session.expunge(folder) - session.commit() self.folder = folder # Apply any post-SQL migration patches. if not is_new: # NOTE: DB_VERSION 6 was first used in v9.5.0-pr1 - if db_version >= 6: + if db_version == 6: self.apply_db6_patches(session) else: pass + # Update DB_VERSION + self.set_prefs(LibraryPrefs.DB_VERSION, LibraryPrefs.DB_VERSION.default) + # everything is fine, set the library path self.library_dir = library_dir return LibraryStatus(success=True, library_path=library_dir) @@ -444,14 +446,27 @@ def apply_db6_patches(self, session: Session): DB_VERSION 6 was first used in v9.5.0-pr1. """ + logger.info("[Library] Applying patches to DB_VERSION: 6 library...") with session: - # Repair "Description" fields with a TEXT_LINE key instead of a TEXT_BOX key - statement = ( + # Repair "Description" fields with a TEXT_LINE key instead of a TEXT_BOX key. + desc_stmd = ( update(ValueType) .where(ValueType.key == _FieldID.DESCRIPTION.name) .values(type=FieldTypeEnum.TEXT_BOX.name) ) - session.execute(statement) + session.execute(desc_stmd) + session.flush() + + # Repair tags that may have a disambiguation_id pointing towards a deleted tag. + all_tag_ids: set[int] = {tag.id for tag in self.tags} + disam_stmt = ( + update(Tag) + .where(Tag.disambiguation_id.not_in(all_tag_ids)) + .values(disambiguation_id=None) + ) + session.execute(disam_stmt) + session.flush() + session.commit() @property @@ -817,6 +832,14 @@ def remove_tag(self, tag: Tag): session.delete(child_tag) session.expunge(child_tag) + disam_stmt = ( + update(Tag) + .where(Tag.disambiguation_id == tag.id) + .values(disambiguation_id=None) + ) + session.execute(disam_stmt) + session.flush() + session.delete(tag) session.commit() session.expunge(tag)