Skip to content

Commit a8db3b0

Browse files
author
Arav K.
committed
[beets.dbcore.db] Rewrite 'Model.store()'
The logic is a bit easier to follow now. See: <beetbox#5337 (comment)>
1 parent c006b42 commit a8db3b0

File tree

1 file changed

+32
-20
lines changed

1 file changed

+32
-20
lines changed

beets/dbcore/db.py

+32-20
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from __future__ import annotations
1818

1919
import contextlib
20+
import itertools
2021
import os
2122
import re
2223
import sqlite3
@@ -580,42 +581,53 @@ def __delattr__(self, key):
580581
# Database interaction (CRUD methods).
581582

582583
def store(self, fields: Optional[Iterable[str]] = None):
583-
"""Save the object's metadata into the library database.
584-
:param fields: the fields to be stored. If not specified, all fields
585-
will be.
586584
"""
587-
if fields is None:
588-
fields = self._fields.keys()
589-
fields = set(fields) - {"id"}
585+
Save the object's metadata into the library database.
586+
587+
:param fields: the fields to be stored (default: all of them).
588+
Only non-flexible fields (excluding `"id"`) can be specified.
589+
"""
590+
590591
db = self._check_db()
591592

592-
# Build assignments for query.
593-
dirty_fields = list(fields & self._dirty)
594-
self._dirty -= fields
595-
assignments = ",".join(f"{k}=?" for k in dirty_fields)
596-
subvars = [self._type(k).to_sql(self[k]) for k in dirty_fields]
593+
# Extract known sets of keys from the table.
594+
known_fields = set(self._fields.keys())
595+
known_flex_fields = set(self._values_flex.keys())
596+
597+
# Normalize the 'fields' parameter.
598+
fields = set(fields or known_fields) - {"id"}
599+
assert (
600+
len(fields & known_flex_fields) == 0
601+
), "`fields` cannot contain flexible fields"
602+
603+
# Compute how various fields were modified.
604+
dirty_fields = fields & self._dirty
605+
dirty_flex_fields = known_flex_fields & self._dirty
606+
removed_flex_fields = self._dirty - fields - known_flex_fields
597607

598608
with db.transaction() as tx:
599-
# Main table update.
600-
if assignments:
609+
# Update non-flexible fields.
610+
if dirty_fields:
611+
# NOTE: the order of iteration of 'dirty_fields' will not change
612+
# between the two statements, since the set is not modified.
613+
assignments = ",".join(f"{k}=?" for k in dirty_fields)
614+
values = [self._type(k).to_sql(self[k]) for k in dirty_fields]
601615
query = f"UPDATE {self._table} SET {assignments} WHERE id=?"
602-
subvars.append(self.id)
603-
tx.mutate(query, subvars)
616+
tx.mutate(query, [*values, self.id])
604617

605618
# Modified/added flexible attributes.
606-
flex_fields = set(self._values_flex.keys())
607-
dirty_flex_fields = list(flex_fields & self._dirty)
608-
self._dirty -= flex_fields
619+
# TODO: Use the underlying 'executemany()' function here.
609620
for key in dirty_flex_fields:
610621
tx.mutate(
611622
f"INSERT INTO {self._flex_table} "
612623
"(entity_id, key, value) "
613-
"VALUES (?, ?, ?);",
624+
"VALUES (?, ?, ?)",
614625
(self.id, key, self._values_flex[key]),
615626
)
616627

617628
# Deleted flexible attributes.
618-
for key in self._dirty:
629+
# TODO: Use the underlying 'executemany()' function here.
630+
for key in removed_flex_fields:
619631
tx.mutate(
620632
f"DELETE FROM {self._flex_table} WHERE entity_id=? AND key=?",
621633
(self.id, key),

0 commit comments

Comments
 (0)