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()
|
self.s.flush()
|
||||||
|
|
||||||
def saveAs(self, newPath):
|
def saveAs(self, newPath):
|
||||||
|
"Returns new deck. Old connection is closed without saving."
|
||||||
oldMediaDir = self.mediaDir()
|
oldMediaDir = self.mediaDir()
|
||||||
self.s.flush()
|
self.s.flush()
|
||||||
# remove new deck if it exists
|
# remove new deck if it exists
|
||||||
|
@ -2270,43 +2271,32 @@ Return new path, relative to media dir."""
|
||||||
os.unlink(newPath)
|
os.unlink(newPath)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
# setup new db tables, then close
|
# copy tables, avoiding implicit commit on current db
|
||||||
newDeck = DeckStorage.Deck(newPath)
|
DeckStorage.Deck(newPath).close()
|
||||||
newDeck.close()
|
new = sqlite.connect(newPath)
|
||||||
# attach new db and copy everything in
|
for table in self.s.column0(
|
||||||
s = self.s.statement
|
"select name from sqlite_master where type = 'table'"):
|
||||||
s("attach database :path as new", path=newPath)
|
if table.startswith("sqlite_"):
|
||||||
s("delete from new.decks")
|
continue
|
||||||
s("delete from new.stats")
|
new.execute("delete from %s" % table)
|
||||||
s("delete from new.tags")
|
cols = [str(x[1]) for x in new.execute(
|
||||||
s("delete from new.cardTags")
|
"pragma table_info('%s')" % table).fetchall()]
|
||||||
s("delete from new.deckVars")
|
q = "select 'insert into %(table)s values("
|
||||||
s("insert into new.decks select * from decks")
|
q += ",".join(["'||quote(\"" + col + "\")||'" for col in cols])
|
||||||
s("insert into new.fieldModels select * from fieldModels")
|
q += ")' from %(table)s"
|
||||||
s("insert into new.modelsDeleted select * from modelsDeleted")
|
q = q % {'table': table}
|
||||||
s("insert into new.cardModels select * from cardModels")
|
for row in self.s.execute(q):
|
||||||
s("insert into new.facts select * from facts")
|
new.execute(row[0])
|
||||||
s("insert into new.fields select * from fields")
|
# save new, close both
|
||||||
s("insert into new.cards select * from cards")
|
new.commit()
|
||||||
s("insert into new.factsDeleted select * from factsDeleted")
|
new.close()
|
||||||
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()
|
|
||||||
self.close()
|
self.close()
|
||||||
# open new db
|
# open again in orm
|
||||||
newDeck = DeckStorage.Deck(newPath)
|
newDeck = DeckStorage.Deck(newPath)
|
||||||
# move media
|
# move media
|
||||||
if oldMediaDir:
|
if oldMediaDir:
|
||||||
newDeck.renameMediaDir(oldMediaDir)
|
newDeck.renameMediaDir(oldMediaDir)
|
||||||
# and return the new deck object
|
# and return the new deck
|
||||||
return newDeck
|
return newDeck
|
||||||
|
|
||||||
# DB maintenance
|
# DB maintenance
|
||||||
|
|
|
@ -61,17 +61,33 @@ def test_saveAs():
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
path2 = "/tmp/test_saveAs2.anki"
|
||||||
|
try:
|
||||||
|
os.unlink(path2)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
# start with an in-memory deck
|
||||||
deck = DeckStorage.Deck()
|
deck = DeckStorage.Deck()
|
||||||
deck.addModel(BasicModel())
|
deck.addModel(BasicModel())
|
||||||
# add a card
|
# add a card
|
||||||
f = deck.newFact()
|
f = deck.newFact()
|
||||||
f['Front'] = u"foo"; f['Back'] = u"bar"
|
f['Front'] = u"foo"; f['Back'] = u"bar"
|
||||||
deck.addFact(f)
|
deck.addFact(f)
|
||||||
|
assert deck.cardCount == 1
|
||||||
# save in new deck
|
# save in new deck
|
||||||
newDeck = deck.saveAs(path)
|
newDeck = deck.saveAs(path)
|
||||||
assert newDeck.cardCount == 1
|
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()
|
newDeck.close()
|
||||||
deck.close()
|
|
||||||
|
|
||||||
def test_factAddDelete():
|
def test_factAddDelete():
|
||||||
deck = DeckStorage.Deck()
|
deck = DeckStorage.Deck()
|
||||||
|
|
Loading…
Reference in a new issue