Skip to content

Commit 480328b

Browse files
authored
fix: patch incorrect description type & invalid disambiguation_id refs (#782)
* fix: update DESCRIPTION to the TEXT_BOX type * fix: remove `disambiguation_id` refs with a tag is deleted
1 parent 6a54323 commit 480328b

File tree

3 files changed

+56
-8
lines changed

3 files changed

+56
-8
lines changed

tagstudio/src/core/enums.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,4 @@ class LibraryPrefs(DefaultEnum):
7171
IS_EXCLUDE_LIST = True
7272
EXTENSION_LIST: list[str] = [".json", ".xmp", ".aae"]
7373
PAGE_SIZE: int = 500
74-
DB_VERSION: int = 6
74+
DB_VERSION: int = 7

tagstudio/src/core/library/alchemy/fields.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class _FieldID(Enum):
113113
AUTHOR = DefaultField(id=1, name="Author", type=FieldTypeEnum.TEXT_LINE)
114114
ARTIST = DefaultField(id=2, name="Artist", type=FieldTypeEnum.TEXT_LINE)
115115
URL = DefaultField(id=3, name="URL", type=FieldTypeEnum.TEXT_LINE)
116-
DESCRIPTION = DefaultField(id=4, name="Description", type=FieldTypeEnum.TEXT_LINE)
116+
DESCRIPTION = DefaultField(id=4, name="Description", type=FieldTypeEnum.TEXT_BOX)
117117
NOTES = DefaultField(id=5, name="Notes", type=FieldTypeEnum.TEXT_BOX)
118118
COLLATION = DefaultField(id=9, name="Collation", type=FieldTypeEnum.TEXT_LINE)
119119
DATE = DefaultField(id=10, name="Date", type=FieldTypeEnum.DATETIME)

tagstudio/src/core/library/alchemy/library.py

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ def open_sqlite_library(self, library_dir: Path, is_new: bool) -> LibraryStatus:
318318
# https://docs.sqlalchemy.org/en/20/changelog/migration_07.html
319319
# Under -> sqlite-the-sqlite-dialect-now-uses-nullpool-for-file-based-databases
320320
poolclass = None if self.storage_path == ":memory:" else NullPool
321+
db_version: int = 0
321322

322323
logger.info(
323324
"[Library] Opening SQLite Library",
@@ -328,11 +329,13 @@ def open_sqlite_library(self, library_dir: Path, is_new: bool) -> LibraryStatus:
328329
with Session(self.engine) as session:
329330
# dont check db version when creating new library
330331
if not is_new:
331-
db_version = session.scalar(
332+
db_result = session.scalar(
332333
select(Preferences).where(Preferences.key == LibraryPrefs.DB_VERSION.name)
333334
)
335+
if db_result:
336+
db_version = db_result.value # type: ignore
334337

335-
if not db_version or db_version.value != LibraryPrefs.DB_VERSION.default:
338+
if db_version < 6: # NOTE: DB_VERSION 6 is the first supported SQL DB version.
336339
mismatch_text = Translations.translate_formatted(
337340
"status.library_version_mismatch"
338341
)
@@ -344,15 +347,14 @@ def open_sqlite_library(self, library_dir: Path, is_new: bool) -> LibraryStatus:
344347
success=False,
345348
message=(
346349
f"{mismatch_text}\n"
347-
f"{found_text} v{0 if not db_version else db_version.value}, "
350+
f"{found_text} v{db_version}, "
348351
f"{expected_text} v{LibraryPrefs.DB_VERSION.default}"
349352
),
350353
)
351354

355+
logger.info(f"[Library] DB_VERSION: {db_version}")
352356
make_tables(self.engine)
353357

354-
# TODO: Determine a good way of updating built-in data after updates.
355-
356358
# Add default tag color namespaces.
357359
if is_new:
358360
namespaces = default_color_groups.namespaces()
@@ -421,14 +423,52 @@ def open_sqlite_library(self, library_dir: Path, is_new: bool) -> LibraryStatus:
421423
)
422424
session.add(folder)
423425
session.expunge(folder)
424-
425426
session.commit()
426427
self.folder = folder
427428

429+
# Apply any post-SQL migration patches.
430+
if not is_new:
431+
# NOTE: DB_VERSION 6 was first used in v9.5.0-pr1
432+
if db_version == 6:
433+
self.apply_db6_patches(session)
434+
else:
435+
pass
436+
437+
# Update DB_VERSION
438+
self.set_prefs(LibraryPrefs.DB_VERSION, LibraryPrefs.DB_VERSION.default)
439+
428440
# everything is fine, set the library path
429441
self.library_dir = library_dir
430442
return LibraryStatus(success=True, library_path=library_dir)
431443

444+
def apply_db6_patches(self, session: Session):
445+
"""Apply migration patches to a library with DB_VERSION 6.
446+
447+
DB_VERSION 6 was first used in v9.5.0-pr1.
448+
"""
449+
logger.info("[Library] Applying patches to DB_VERSION: 6 library...")
450+
with session:
451+
# Repair "Description" fields with a TEXT_LINE key instead of a TEXT_BOX key.
452+
desc_stmd = (
453+
update(ValueType)
454+
.where(ValueType.key == _FieldID.DESCRIPTION.name)
455+
.values(type=FieldTypeEnum.TEXT_BOX.name)
456+
)
457+
session.execute(desc_stmd)
458+
session.flush()
459+
460+
# Repair tags that may have a disambiguation_id pointing towards a deleted tag.
461+
all_tag_ids: set[int] = {tag.id for tag in self.tags}
462+
disam_stmt = (
463+
update(Tag)
464+
.where(Tag.disambiguation_id.not_in(all_tag_ids))
465+
.values(disambiguation_id=None)
466+
)
467+
session.execute(disam_stmt)
468+
session.flush()
469+
470+
session.commit()
471+
432472
@property
433473
def default_fields(self) -> list[BaseField]:
434474
with Session(self.engine) as session:
@@ -793,6 +833,14 @@ def remove_tag(self, tag: Tag):
793833
session.delete(child_tag)
794834
session.expunge(child_tag)
795835

836+
disam_stmt = (
837+
update(Tag)
838+
.where(Tag.disambiguation_id == tag.id)
839+
.values(disambiguation_id=None)
840+
)
841+
session.execute(disam_stmt)
842+
session.flush()
843+
796844
session.delete(tag)
797845
session.commit()
798846
session.expunge(tag)

0 commit comments

Comments
 (0)