mirror of
https://github.com/ankitects/anki.git
synced 2025-09-23 16:26:40 -04:00
implement save as without attaching a db
Attaching new to old causes old to be implicitly commited. We also can't access old from new if a write lock has been taken out, so the only option left is to move all the data through Python and take the speed hit.
This commit is contained in:
parent
dfd88cc4c1
commit
095a7dbb6f
2 changed files with 39 additions and 33 deletions
54
anki/deck.py
54
anki/deck.py
|
@ -2263,6 +2263,7 @@ Return new path, relative to media dir."""
|
|||
self.s.flush()
|
||||
|
||||
def saveAs(self, newPath):
|
||||
"Returns new deck. Old connection is closed without saving."
|
||||
oldMediaDir = self.mediaDir()
|
||||
self.s.flush()
|
||||
# remove new deck if it exists
|
||||
|
@ -2270,43 +2271,32 @@ Return new path, relative to media dir."""
|
|||
os.unlink(newPath)
|
||||
except OSError:
|
||||
pass
|
||||
# setup new db tables, then close
|
||||
newDeck = DeckStorage.Deck(newPath)
|
||||
newDeck.close()
|
||||
# attach new db and copy everything in
|
||||
s = self.s.statement
|
||||
s("attach database :path as new", path=newPath)
|
||||
s("delete from new.decks")
|
||||
s("delete from new.stats")
|
||||
s("delete from new.tags")
|
||||
s("delete from new.cardTags")
|
||||
s("delete from new.deckVars")
|
||||
s("insert into new.decks select * from decks")
|
||||
s("insert into new.fieldModels select * from fieldModels")
|
||||
s("insert into new.modelsDeleted select * from modelsDeleted")
|
||||
s("insert into new.cardModels select * from cardModels")
|
||||
s("insert into new.facts select * from facts")
|
||||
s("insert into new.fields select * from fields")
|
||||
s("insert into new.cards select * from cards")
|
||||
s("insert into new.factsDeleted select * from factsDeleted")
|
||||
s("insert into new.reviewHistory select * from reviewHistory")
|
||||
s("insert into new.cardsDeleted select * from cardsDeleted")
|
||||
s("insert into new.models select * from models")
|
||||
s("insert into new.stats select * from stats")
|
||||
s("insert into new.media select * from media")
|
||||
s("insert into new.tags select * from tags")
|
||||
s("insert into new.cardTags select * from cardTags")
|
||||
s("insert into new.deckVars select * from deckVars")
|
||||
s("detach database new")
|
||||
# close ourselves
|
||||
self.s.commit()
|
||||
# copy tables, avoiding implicit commit on current db
|
||||
DeckStorage.Deck(newPath).close()
|
||||
new = sqlite.connect(newPath)
|
||||
for table in self.s.column0(
|
||||
"select name from sqlite_master where type = 'table'"):
|
||||
if table.startswith("sqlite_"):
|
||||
continue
|
||||
new.execute("delete from %s" % table)
|
||||
cols = [str(x[1]) for x in new.execute(
|
||||
"pragma table_info('%s')" % table).fetchall()]
|
||||
q = "select 'insert into %(table)s values("
|
||||
q += ",".join(["'||quote(\"" + col + "\")||'" for col in cols])
|
||||
q += ")' from %(table)s"
|
||||
q = q % {'table': table}
|
||||
for row in self.s.execute(q):
|
||||
new.execute(row[0])
|
||||
# save new, close both
|
||||
new.commit()
|
||||
new.close()
|
||||
self.close()
|
||||
# open new db
|
||||
# open again in orm
|
||||
newDeck = DeckStorage.Deck(newPath)
|
||||
# move media
|
||||
if oldMediaDir:
|
||||
newDeck.renameMediaDir(oldMediaDir)
|
||||
# and return the new deck object
|
||||
# and return the new deck
|
||||
return newDeck
|
||||
|
||||
# DB maintenance
|
||||
|
|
|
@ -61,17 +61,33 @@ def test_saveAs():
|
|||
os.unlink(path)
|
||||
except OSError:
|
||||
pass
|
||||
path2 = "/tmp/test_saveAs2.anki"
|
||||
try:
|
||||
os.unlink(path2)
|
||||
except OSError:
|
||||
pass
|
||||
# start with an in-memory deck
|
||||
deck = DeckStorage.Deck()
|
||||
deck.addModel(BasicModel())
|
||||
# add a card
|
||||
f = deck.newFact()
|
||||
f['Front'] = u"foo"; f['Back'] = u"bar"
|
||||
deck.addFact(f)
|
||||
assert deck.cardCount == 1
|
||||
# save in new deck
|
||||
newDeck = deck.saveAs(path)
|
||||
assert newDeck.cardCount == 1
|
||||
# delete card
|
||||
id = newDeck.s.scalar("select id from cards")
|
||||
newDeck.deleteCard(id)
|
||||
# save into new deck
|
||||
newDeck2 = newDeck.saveAs(path2)
|
||||
# new deck should have zero cards
|
||||
assert newDeck2.cardCount == 0
|
||||
# but old deck should have reverted the unsaved changes
|
||||
newDeck = DeckStorage.Deck(path)
|
||||
assert newDeck.cardCount == 1
|
||||
newDeck.close()
|
||||
deck.close()
|
||||
|
||||
def test_factAddDelete():
|
||||
deck = DeckStorage.Deck()
|
||||
|
|
Loading…
Reference in a new issue