diff --git a/migration/foreign_keys.6.cr b/migration/foreign_keys.6.cr new file mode 100644 index 0000000..8cc00f2 --- /dev/null +++ b/migration/foreign_keys.6.cr @@ -0,0 +1,85 @@ +class ForeignKeys < MG::Base + def up : String + <<-SQL + -- add foreign key to tags + ALTER TABLE tags RENAME TO tmp; + + CREATE TABLE tags ( + id TEXT NOT NULL, + tag TEXT NOT NULL, + UNIQUE (id, tag), + FOREIGN KEY (id) REFERENCES titles (id) + ON UPDATE CASCADE + ON DELETE CASCADE + ); + + INSERT INTO tags + SELECT * FROM tmp; + + DROP TABLE tmp; + + CREATE INDEX tags_id_idx ON tags (id); + CREATE INDEX tags_tag_idx ON tags (tag); + + -- add foreign key to thumbnails + ALTER TABLE thumbnails RENAME TO tmp; + + CREATE TABLE thumbnails ( + id TEXT NOT NULL, + data BLOB NOT NULL, + filename TEXT NOT NULL, + mime TEXT NOT NULL, + size INTEGER NOT NULL, + FOREIGN KEY (id) REFERENCES ids (id) + ON UPDATE CASCADE + ON DELETE CASCADE + ); + + INSERT INTO thumbnails + SELECT * FROM tmp; + + DROP TABLE tmp; + + CREATE UNIQUE INDEX tn_index ON thumbnails (id); + SQL + end + + def down : String + <<-SQL + -- remove foreign key from thumbnails + ALTER TABLE thumbnails RENAME TO tmp; + + CREATE TABLE thumbnails ( + id TEXT NOT NULL, + data BLOB NOT NULL, + filename TEXT NOT NULL, + mime TEXT NOT NULL, + size INTEGER NOT NULL + ); + + INSERT INTO thumbnails + SELECT * FROM tmp; + + DROP TABLE tmp; + + CREATE UNIQUE INDEX tn_index ON thumbnails (id); + + -- remove foreign key from tags + ALTER TABLE tags RENAME TO tmp; + + CREATE TABLE tags ( + id TEXT NOT NULL, + tag TEXT NOT NULL, + UNIQUE (id, tag) + ); + + INSERT INTO tags + SELECT * FROM tmp; + + DROP TABLE tmp; + + CREATE INDEX tags_id_idx ON tags (id); + CREATE INDEX tags_tag_idx ON tags (tag); + SQL + end +end diff --git a/src/storage.cr b/src/storage.cr index 06ce534..2d87025 100644 --- a/src/storage.cr +++ b/src/storage.cr @@ -78,9 +78,11 @@ class Storage private def get_db(&block : DB::Database ->) if @db.nil? DB.open "sqlite3://#{@path}" do |db| + db.exec "PRAGMA foreign_keys = 1" yield db end else + @db.not_nil!.exec "PRAGMA foreign_keys = 1" yield @db.not_nil! end end @@ -361,6 +363,7 @@ class Storage MainFiber.run do Logger.info "Starting DB optimization" get_db do |db| + # Delete dangling entry IDs trash_ids = [] of String db.query "select path, id from ids" do |rs| rs.each do @@ -369,29 +372,24 @@ class Storage end end - # Delete dangling IDs db.exec "delete from ids where id in " \ "(#{trash_ids.map { |i| "'#{i}'" }.join ","})" - Logger.debug "#{trash_ids.size} dangling IDs deleted" \ + Logger.debug "#{trash_ids.size} dangling entry IDs deleted" \ if trash_ids.size > 0 - # Delete dangling thumbnails - trash_thumbnails_count = db.query_one "select count(*) from " \ - "thumbnails where id not in " \ - "(select id from ids)", as: Int32 - if trash_thumbnails_count > 0 - db.exec "delete from thumbnails where id not in (select id from ids)" - Logger.info "#{trash_thumbnails_count} dangling thumbnails deleted" + # Delete dangling title IDs + trash_titles = [] of String + db.query "select path, id from titles" do |rs| + rs.each do + path = rs.read String + trash_titles << rs.read String unless Dir.exists? path + end end - # Delete dangling tags - trash_tags_count = db.query_one "select count(*) from tags " \ - "where id not in " \ - "(select id from ids)", as: Int32 - if trash_tags_count > 0 - db.exec "delete from tags where id not in (select id from ids)" - Logger.info "#{trash_tags_count} dangling tags deleted" - end + db.exec "delete from titles where id in " \ + "(#{trash_titles.map { |i| "'#{i}'" }.join ","})" + Logger.debug "#{trash_titles.size} dangling title IDs deleted" \ + if trash_titles.size > 0 end Logger.info "DB optimization finished" end