restore the deletion log

the initial plan was to zero the creation time and leave the cards/facts there
until we have a chance to garbage collect them on a schema change, but such an
approach won't work with deck subscriptions
This commit is contained in:
Damien Elmes 2011-05-04 19:00:38 +09:00
parent f2604b7805
commit 3d370f675b
6 changed files with 39 additions and 62 deletions

View file

@ -10,7 +10,7 @@ from anki.utils import intTime, hexifyID
# Type: 0=new, 1=learning, 2=due
# Queue: same as above, and:
# -1=suspended, -2=user buried, -3=sched buried, -4=deleted
# -1=suspended, -2=user buried, -3=sched buried
# Due is used differently for different queues.
# - new queue: fact id or random int
# - rev queue: integer day

View file

@ -20,6 +20,10 @@ REV_CARDS_OLD_FIRST = 0
REV_CARDS_NEW_FIRST = 1
REV_CARDS_RANDOM = 2
# deletion types
DEL_CARD = 0
DEL_FACT = 1
# Labels
##########################################################################

View file

@ -152,8 +152,6 @@ qconf=?, conf=?, data=?""",
if not self.schemaChanged():
if check and not runFilter("modSchema", True):
raise AnkiError("abortSchemaMod")
# next sync will be full
self.emptyTrash()
self.scm = intTime()
def schemaChanged(self):
@ -213,11 +211,18 @@ qconf=?, conf=?, data=?""",
self.modelCache = {}
self.sched.reset()
# Deletion logging
##########################################################################
def _logDels(self, ids, type):
self.db.executemany("insert into graves values (%d, ?, %d)" % (
intTime(), type), ([x] for x in ids))
# Facts
##########################################################################
def factCount(self):
return self.db.scalar("select count() from facts where crt != 0")
return self.db.scalar("select count() from facts")
def newFact(self):
"Return a new fact with the current model."
@ -251,13 +256,7 @@ qconf=?, conf=?, data=?""",
strids = ids2str(ids)
self.db.execute("delete from facts where id in %s" % strids)
self.db.execute("delete from fsums where fid in %s" % strids)
def _delDanglingFacts(self):
"Delete any facts without cards. Don't call this directly."
ids = self.db.list("""
select id from facts where id not in (select distinct fid from cards)""")
self._delFacts(ids)
return ids
self._logDels(ids, DEL_FACT)
# Card creation
##########################################################################
@ -341,41 +340,23 @@ select id from facts where id not in (select distinct fid from cards)""")
##########################################################################
def cardCount(self):
return self.db.scalar("select count() from cards where crt != 0")
def delCard(self, id):
"Delete a card given its id. Delete any unused facts."
self.delCards([id])
return self.db.scalar("select count() from cards")
def delCards(self, ids):
"Bulk delete cards by ID."
if not ids:
return
sids = ids2str(ids)
if self.schemaChanged():
# immediate delete?
self.db.execute("delete from cards where id in %s" % sids)
self.db.execute("delete from revlog where cid in %s" % sids)
# remove any dangling facts
self._delDanglingFacts()
else:
# trash
sfids = ids2str(
self.db.list("select fid from cards where id in "+sids))
# need to handle delete of fsums/revlog remotely after sync
self.db.execute(
"update cards set crt = 0, queue = -4, mod = ? where id in "+sids,
intTime())
self.db.execute(
"update facts set crt = 0, mod = ? where id in "+sfids,
intTime())
self.db.execute("delete from fsums where fid in "+sfids)
fids = self.db.list("select fid from cards where id in "+sids)
# remove cards
self.db.execute("delete from cards where id in "+sids)
self.db.execute("delete from revlog where cid in "+sids)
def emptyTrash(self):
self.db.executescript("""
delete from facts where id in (select fid from cards where queue = -4);
delete from cards where queue = -4;""")
self._logDels(ids, DEL_CARD)
# then facts
fids = self.db.list("""
select id from facts where id in %s and id not in (select fid from cards)""" %
ids2str(fids))
self._delFacts(fids)
# Models
##########################################################################
@ -783,6 +764,10 @@ update facts set tags = :t, mod = :n where id = :id""", [fix(row) for row in res
problems = []
self.save()
oldSize = os.stat(self.path)[stat.ST_SIZE]
# delete any facts with missing cards
ids = self.db.list("""
select id from facts where id not in (select distinct fid from cards)""")
self._delFacts(ids)
# tags
self.db.execute("delete from tags")
self.updateFactTags()

View file

@ -135,6 +135,12 @@ create table if not exists gconf (
conf text not null
);
create table if not exists graves (
time integer not null,
oid integer not null,
type integer not null
);
create table if not exists revlog (
time integer primary key,
cid integer not null,

View file

@ -53,12 +53,10 @@ def test_delete():
f['Back'] = u'2'
deck.addFact(f)
cid = f.cards()[0].id
# when the schema is dirty, deletion should be immediate
assert deck.schemaChanged() == True
deck.reset()
deck.sched.answerCard(deck.sched.getCard(), 2)
assert deck.db.scalar("select count() from revlog") == 1
deck.delCard(cid)
deck.delCards([cid])
assert deck.cardCount() == 0
assert deck.factCount() == 0
assert deck.db.scalar("select count() from facts") == 0
@ -68,26 +66,10 @@ def test_delete():
# add the fact back
deck.addFact(f)
assert deck.cardCount() == 1
# mark the schema as clean
deck.lastSync = deck.scm + 1
# and deck as syncable
deck.syncName = "abc"
# cards/facts should go in the deletion log instead
cid = f.cards()[0].id
deck.delCard(cid)
deck.delCards([cid])
assert deck.cardCount() == 0
assert deck.factCount() == 0
assert deck.db.scalar("select count() from facts") == 1
assert deck.db.scalar("select count() from cards") == 1
assert deck.db.scalar("select 1 from cards where crt = 0") == 1
assert deck.db.scalar("select 1 from facts where crt = 0") == 1
assert deck.db.scalar("select queue from cards") == -4
# modifying the schema should empty the trash
deck.modSchema()
assert deck.cardCount() == 0
assert deck.factCount() == 0
assert deck.db.scalar("select count() from facts") == 0
assert deck.db.scalar("select count() from cards") == 0
def test_misc():
d = getEmptyDeck()

View file

@ -74,11 +74,11 @@ def test_factAddDelete():
id1 = cards[0].id; id2 = cards[1].id
assert deck.cardCount() == 2
assert deck.factCount() == 1
deck.delCard(id1)
deck.delCards([id1])
assert deck.cardCount() == 1
assert deck.factCount() == 1
# and the second should clear the fact
deck.delCard(id2)
deck.delCards([id2])
assert deck.cardCount() == 0
assert deck.factCount() == 0