From 74383e3c3c12f72be1481ab0b86c7360b95c2d85 Mon Sep 17 00:00:00 2001 From: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com> Date: Sat, 23 Aug 2025 18:11:33 -0700 Subject: [PATCH] feat: swap IDs in tag_parents table; bump DB to v100 commit c1346e7df36b137cf88be284a96329fee9605a6a Author: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com> Date: Sat Aug 23 18:04:58 2025 -0700 docs: update DB v100 with tag_parents flip commit 7e5d9381759b000533c809df9d9bc4f9d984e363 Author: HeikoWasTaken Date: Sun Aug 24 00:31:21 2025 +0100 fix: swap IDs in parent_tags DB table (#998) * fix: reorder child and parent IDs in TagParent constructor call * feat: add db10 migration * fix: SQL query returning parent IDs instead of children IDs * fix: stop assigning child tags as parents * fix: select and remove parent tags, instead of child tags * test/fix: correctly reorder child/parent args in broken test * fix: migrate json subtags as parent tags, instead of child tags (I see where it went wrong now lol) * fix: query parent tags instead of children * refactor: scooching this down below db9 migrations * test: add DB10 migration test --------- Co-authored-by: heiko commit 1ce02699ad9798800f9d98832b2a6377e3d79ed4 Author: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com> Date: Sat Aug 23 14:47:39 2025 -0700 feat: add db minor versioning, bump to 100 --- docs/updates/schema_changes.md | 19 +++- src/tagstudio/core/enums.py | 2 +- src/tagstudio/core/library/alchemy/library.py | 105 +++++++++++++----- src/tagstudio/core/library/alchemy/models.py | 4 +- .../core/library/alchemy/visitors.py | 12 +- src/tagstudio/qt/widgets/migration_modal.py | 2 +- .../.TagStudio/ts_library.sqlite | Bin 0 -> 98304 bytes .../.TagStudio/ts_library.sqlite | Bin 98304 -> 98304 bytes tests/qt/test_build_tag_panel.py | 2 +- tests/test_db_migrations.py | 1 + 10 files changed, 104 insertions(+), 43 deletions(-) create mode 100644 tests/fixtures/empty_libraries/DB_VERSION_100/.TagStudio/ts_library.sqlite diff --git a/docs/updates/schema_changes.md b/docs/updates/schema_changes.md index f9be851e..aa848837 100644 --- a/docs/updates/schema_changes.md +++ b/docs/updates/schema_changes.md @@ -71,8 +71,21 @@ Migration from the legacy JSON format is provided via a walkthrough when opening ### Version 9 -| Used From | Used Until | Format | Location | -| ----------------------------------------------------------------------- | ---------- | ------ | ----------------------------------------------- | -| [v9.5.2](https://github.com/TagStudioDev/TagStudio/releases/tag/v9.5.2) | _Current_ | SQLite | ``/.TagStudio/ts_library.sqlite | +| Used From | Used Until | Format | Location | +| ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------ | ----------------------------------------------- | +| [v9.5.2](https://github.com/TagStudioDev/TagStudio/releases/tag/v9.5.2) | [v9.5.3](https://github.com/TagStudioDev/TagStudio/releases/tag/v9.5.3) | SQLite | ``/.TagStudio/ts_library.sqlite | - Adds the `filename` column to the `entries` table. Used for sorting entries by filename in search results. + +--- + +### Version 100 + +| Used From | Used Until | Format | Location | +| ----------------------------------------------------------------------- | ---------- | ------ | ----------------------------------------------- | +| [v9.5.4](https://github.com/TagStudioDev/TagStudio/releases/tag/v9.5.4) | _Current_ | SQLite | ``/.TagStudio/ts_library.sqlite | + +- Introduces built-in minor versioning + - The version number divided by 100 (and floored) constitutes the **major** version. Major version indicate breaking changes that prevent libraries from being opened in TagStudio versions older than the ones they were created in. + - Values more precise than this ("ones" through "tens" columns) constitute the **minor** version. These indicate minor changes that don't prevent a newer library from being opened in an older version of TagStudio, as long as the major version is not also increased. +- Swaps `parent_id` and `child_id` values in the `tag_parents` table, which have erroneously been flipped since the first SQLite DB version. diff --git a/src/tagstudio/core/enums.py b/src/tagstudio/core/enums.py index 467dab7f..8432bbb8 100644 --- a/src/tagstudio/core/enums.py +++ b/src/tagstudio/core/enums.py @@ -79,9 +79,9 @@ class DefaultEnum(enum.Enum): raise AttributeError("access the value via .default property instead") +# TODO: Remove DefaultEnum and LibraryPrefs classes once remaining values are removed. class LibraryPrefs(DefaultEnum): """Library preferences with default value accessible via .default property.""" IS_EXCLUDE_LIST = True EXTENSION_LIST = [".json", ".xmp", ".aae"] - DB_VERSION = 9 diff --git a/src/tagstudio/core/library/alchemy/library.py b/src/tagstudio/core/library/alchemy/library.py index bdc0dad4..08d9f66e 100644 --- a/src/tagstudio/core/library/alchemy/library.py +++ b/src/tagstudio/core/library/alchemy/library.py @@ -12,7 +12,7 @@ from dataclasses import dataclass from datetime import UTC, datetime from os import makedirs from pathlib import Path -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from uuid import uuid4 from warnings import catch_warnings @@ -92,6 +92,8 @@ if TYPE_CHECKING: logger = structlog.get_logger(__name__) +DB_VERSION_KEY: str = "DB_VERSION" +DB_VERSION: int = 100 TAG_CHILDREN_QUERY = text(""" -- Note for this entire query that tag_parents.child_id is the parent id and tag_parents.parent_id is the child id due to bad naming @@ -273,8 +275,8 @@ class Library: # Parent Tags (Previously known as "Subtags" in JSON) for tag in json_lib.tags: - for child_id in tag.subtag_ids: - self.add_parent_tag(parent_id=tag.id, child_id=child_id) + for parent_id in tag.subtag_ids: + self.add_parent_tag(parent_id=parent_id, child_id=tag.id) # Entries self.add_entries( @@ -365,7 +367,7 @@ class Library: # 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 + loaded_db_version: int = 0 logger.info( "[Library] Opening SQLite Library", @@ -377,13 +379,21 @@ class Library: # dont check db version when creating new library if not is_new: db_result = session.scalar( - select(Preferences).where(Preferences.key == LibraryPrefs.DB_VERSION.name) + select(Preferences).where(Preferences.key == DB_VERSION_KEY) ) if db_result: - db_version = db_result.value + assert isinstance(db_result.value, int) + loaded_db_version = db_result.value - # NOTE: DB_VERSION 6 is the first supported SQL DB version. - if db_version < 6 or db_version > LibraryPrefs.DB_VERSION.default: + # ======================== Library Database Version Checking ======================= + # DB_VERSION 6 is the first supported SQLite DB version. + # If the DB_VERSION is >= 100, that means it's a compound major + minor version. + # - Dividing by 100 and flooring gives the major (breaking changes) version. + # - If a DB has major version higher than the current program, don't load it. + # - If only the minor version is higher, it's still allowed to load. + if loaded_db_version < 6 or ( + loaded_db_version >= 100 and loaded_db_version // 100 > DB_VERSION // 100 + ): mismatch_text = Translations["status.library_version_mismatch"] found_text = Translations["status.library_version_found"] expected_text = Translations["status.library_version_expected"] @@ -391,12 +401,12 @@ class Library: success=False, message=( f"{mismatch_text}\n" - f"{found_text} v{db_version}, " - f"{expected_text} v{LibraryPrefs.DB_VERSION.default}" + f"{found_text} v{loaded_db_version}, " + f"{expected_text} v{DB_VERSION}" ), ) - logger.info(f"[Library] DB_VERSION: {db_version}") + logger.info(f"[Library] DB_VERSION: {loaded_db_version}") make_tables(self.engine) # Add default tag color namespaces. @@ -434,6 +444,15 @@ class Library: except IntegrityError: session.rollback() + # TODO: Completely rework this "preferences" system. + with catch_warnings(record=True): + try: + session.add(Preferences(key=DB_VERSION_KEY, value=DB_VERSION)) + session.commit() + except IntegrityError: + logger.debug("preference already exists", pref=DB_VERSION_KEY) + session.rollback() + for pref in LibraryPrefs: with catch_warnings(record=True): try: @@ -474,28 +493,30 @@ class Library: # Apply any post-SQL migration patches. if not is_new: # save backup if patches will be applied - if LibraryPrefs.DB_VERSION.default != db_version: + if loaded_db_version != DB_VERSION: self.library_dir = library_dir self.save_library_backup_to_disk() self.library_dir = None # schema changes first - if db_version < 8: + if loaded_db_version < 8: self.apply_db8_schema_changes(session) - if db_version < 9: + if loaded_db_version < 9: self.apply_db9_schema_changes(session) # now the data changes - if db_version == 6: + if loaded_db_version == 6: self.apply_repairs_for_db6(session) - if db_version >= 6 and db_version < 8: + if loaded_db_version >= 6 and loaded_db_version < 8: self.apply_db8_default_data(session) - if db_version < 9: + if loaded_db_version < 9: self.apply_db9_filename_population(session) + if loaded_db_version < 100: + self.apply_db100_parent_repairs(session) # Update DB_VERSION - if LibraryPrefs.DB_VERSION.default > db_version: - self.set_prefs(LibraryPrefs.DB_VERSION, LibraryPrefs.DB_VERSION.default) + if loaded_db_version < DB_VERSION: + self.set_prefs(DB_VERSION_KEY, DB_VERSION) # everything is fine, set the library path self.library_dir = library_dir @@ -617,6 +638,20 @@ class Library: session.commit() logger.info("[Library][Migration] Populated filename column in entries table") + def apply_db100_parent_repairs(self, session: Session): + """Apply database repairs introduced in DB_VERSION 100.""" + logger.info("[Library][Migration] Applying patches to DB_VERSION 100 library...") + with session: + # Repair parent-child tag relationships that are the wrong way around. + stmt = update(TagParent).values( + parent_id=TagParent.child_id, + child_id=TagParent.parent_id, + ) + session.execute(stmt) + session.flush() + + session.commit() + @property def default_fields(self) -> list[BaseField]: with Session(self.engine) as session: @@ -1631,35 +1666,49 @@ class Library: # load all tag's parent tags to know which to remove prev_parent_tags = session.scalars( - select(TagParent).where(TagParent.parent_id == tag.id) + select(TagParent).where(TagParent.child_id == tag.id) ).all() for parent_tag in prev_parent_tags: - if parent_tag.child_id not in parent_ids: + if parent_tag.parent_id not in parent_ids: session.delete(parent_tag) else: # no change, remove from list - parent_ids.remove(parent_tag.child_id) + parent_ids.remove(parent_tag.parent_id) # create remaining items for parent_id in parent_ids: # add new parent tag parent_tag = TagParent( - parent_id=tag.id, - child_id=parent_id, + parent_id=parent_id, + child_id=tag.id, ) session.add(parent_tag) - def prefs(self, key: LibraryPrefs): + def prefs(self, key: str | LibraryPrefs): # load given item from Preferences table with Session(self.engine) as session: - return session.scalar(select(Preferences).where(Preferences.key == key.name)).value + if isinstance(key, LibraryPrefs): + return session.scalar(select(Preferences).where(Preferences.key == key.name)).value + else: + return session.scalar(select(Preferences).where(Preferences.key == key)).value - def set_prefs(self, key: LibraryPrefs, value) -> None: + def set_prefs(self, key: str | LibraryPrefs, value: Any) -> None: # set given item in Preferences table with Session(self.engine) as session: # load existing preference and update value - pref = session.scalar(select(Preferences).where(Preferences.key == key.name)) + pref: Preferences | None + + stuff = session.scalars(select(Preferences)) + logger.info([x.key for x in list(stuff)]) + + if isinstance(key, LibraryPrefs): + pref = session.scalar(select(Preferences).where(Preferences.key == key.name)) + else: + pref = session.scalar(select(Preferences).where(Preferences.key == key)) + + logger.info("loading pref", pref=pref, key=key, value=value) + assert pref is not None pref.value = value session.add(pref) session.commit() diff --git a/src/tagstudio/core/library/alchemy/models.py b/src/tagstudio/core/library/alchemy/models.py index f85a02a4..cd63f4ab 100644 --- a/src/tagstudio/core/library/alchemy/models.py +++ b/src/tagstudio/core/library/alchemy/models.py @@ -99,8 +99,8 @@ class Tag(Base): aliases: Mapped[set[TagAlias]] = relationship(back_populates="tag") parent_tags: Mapped[set["Tag"]] = relationship( secondary=TagParent.__tablename__, - primaryjoin="Tag.id == TagParent.parent_id", - secondaryjoin="Tag.id == TagParent.child_id", + primaryjoin="Tag.id == TagParent.child_id", + secondaryjoin="Tag.id == TagParent.parent_id", back_populates="parent_tags", ) disambiguation_id: Mapped[int | None] diff --git a/src/tagstudio/core/library/alchemy/visitors.py b/src/tagstudio/core/library/alchemy/visitors.py index b3d173e3..bf2e5b5e 100644 --- a/src/tagstudio/core/library/alchemy/visitors.py +++ b/src/tagstudio/core/library/alchemy/visitors.py @@ -31,17 +31,15 @@ else: logger = structlog.get_logger(__name__) -# TODO: Reevaluate after subtags -> parent tags name change TAG_CHILDREN_ID_QUERY = text(""" --- Note for this entire query that tag_parents.child_id is the parent id and tag_parents.parent_id is the child id due to bad naming WITH RECURSIVE ChildTags AS ( - SELECT :tag_id AS child_id + SELECT :tag_id AS tag_id UNION - SELECT tp.parent_id AS child_id - FROM tag_parents tp - INNER JOIN ChildTags c ON tp.child_id = c.child_id + SELECT tp.child_id AS tag_id + FROM tag_parents tp + INNER JOIN ChildTags c ON tp.parent_id = c.tag_id ) -SELECT child_id FROM ChildTags; +SELECT tag_id FROM ChildTags; """) # noqa: E501 diff --git a/src/tagstudio/qt/widgets/migration_modal.py b/src/tagstudio/qt/widgets/migration_modal.py index 975c738b..765c782a 100644 --- a/src/tagstudio/qt/widgets/migration_modal.py +++ b/src/tagstudio/qt/widgets/migration_modal.py @@ -641,7 +641,7 @@ class JsonMigrationModal(QObject): for tag in self.sql_lib.tags: tag_id = tag.id # Tag IDs start at 0 sql_parent_tags = set( - session.scalars(select(TagParent.child_id).where(TagParent.parent_id == tag.id)) + session.scalars(select(TagParent.parent_id).where(TagParent.child_id == tag.id)) ) # JSON tags allowed self-parenting; SQL tags no longer allow this. diff --git a/tests/fixtures/empty_libraries/DB_VERSION_100/.TagStudio/ts_library.sqlite b/tests/fixtures/empty_libraries/DB_VERSION_100/.TagStudio/ts_library.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..43e909be6abed17deeac1392adc9b8aabf8d48ea GIT binary patch literal 98304 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 literal 0 HcmV?d00001 diff --git a/tests/fixtures/search_library/.TagStudio/ts_library.sqlite b/tests/fixtures/search_library/.TagStudio/ts_library.sqlite index b47e91da9eabad64da9fb0ebed3d6a9c01bc7ce0..0f0a9154536ed64b5d4ece35cedc316be3be67ee 100644 GIT binary patch delta 1595 zcmXw&L2OK26vy9t_jXRFGxy$^7DZcCMNzF5RTNcKRTdUsEG&eCL@X>s!b0TLLM$xG z!a^)8#6m(ux)2Ksk+2XF3$d_}Z}Ba3rf;S*ok`!!bmlvEPS5U_-|sALZt|a=j!aKS z9=5sPAGMD+e0Tb9K6rb1a>l(p`)cO%YrjP{!<6tmX%J3YXqHKsB$I>_1R5p~L|Q61 zZXrY%w<&>e%$j8xhcUW|!%<3c7_}xZV{n903=UI@!69n`U%D`2m4k4QnhOVPTb2&& zx3GXPOv!j`0xH8oL%-E>ohK}uB^AXtW7HU**r{dD73ppRMwc9JSX zuZ2*QVF#r$Y^PL$ZInvTL#YH?ttp5Qw$M!on<<6RZB1D$!6pkKLKmea*l0~4iavBw zE5Zh9Md%>QU&$Pbm@$GWQ2a8f9P3#OP^C0^h^C%U(;vwh;C6S^-bN-QT1M(R%5Eq{55~f zNArBZ+%vb$c{6H;Osfv{fBLunpr7fx`jVc|J-S2H)W7PJdZF&Ct7=$v&vqs5$Ni}E zJXhj&P7&dFrigGoC)p(@ne>S|*#S)u;ew7+#R-j*!wntdyZ9K__)&5=qtTV>%h(al zvBM+-=^i47QySsw9;Aw6IzWVL+RxP)Ua6wCZ>5Ub&`K4xz2tCGd-$%p+mi3fgs2Yk z;qm}w+|@2Vtn_oO^sy`NB*JO+axL%RTH4OFw2f=2haB!}E8m4%qJI7R6C&J95;xXO zgd^KjCw$;i7df2SMy|z9uEh;Zg$}B?w03rjZRBuk>xpn|>$v*)wIp$Gt(N+}Es#({xoqKR?P#5gbz z^VY#II1~m3!r)*WOeDs1APf%1L*|sERsEs9RS(pU>aMz_zEYp4kJSzJrg}@guP&(>HK|6_;PQaI(UnzXnkGo5 zb}`Bp)a793<_27UJ3$RqPFtc8G<(_C&RIS2B0~qL6kMAEL3rsA6Z3v;!<;wj-+89whBH z7DDT1p|rjrX?s~{ZEIA%B}is=GZVe_MCF@;q}|9uavNA^t`^m-21(n^LUlGOH$l>B z7Q$03l-K3;HX>|xhC^vPSg5ZORc{Y+Y5LnjfaR!qTM)uyAwf}=?MafRZL*TwU%Q;7 GW3K^lwisLh diff --git a/tests/qt/test_build_tag_panel.py b/tests/qt/test_build_tag_panel.py index 04f77ff0..014843cb 100644 --- a/tests/qt/test_build_tag_panel.py +++ b/tests/qt/test_build_tag_panel.py @@ -79,7 +79,7 @@ def test_build_tag_panel_set_parent_tags(library, generate_tag): assert parent assert child - library.add_parent_tag(child.id, parent.id) + library.add_parent_tag(parent.id, child.id) child = library.get_tag(child.id) diff --git a/tests/test_db_migrations.py b/tests/test_db_migrations.py index ffc0744b..21f7a046 100644 --- a/tests/test_db_migrations.py +++ b/tests/test_db_migrations.py @@ -23,6 +23,7 @@ EMPTY_LIBRARIES = "empty_libraries" str(Path(CWD.parent / FIXTURES / EMPTY_LIBRARIES / "DB_VERSION_7")), str(Path(CWD.parent / FIXTURES / EMPTY_LIBRARIES / "DB_VERSION_8")), str(Path(CWD.parent / FIXTURES / EMPTY_LIBRARIES / "DB_VERSION_9")), + str(Path(CWD.parent / FIXTURES / EMPTY_LIBRARIES / "DB_VERSION_100")), ], ) def test_library_migrations(path: str):