using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace Duplicati.Library.Main.Database { public partial class LocalBlocklistUpdateDatabase : LocalRestoreDatabase { protected string m_tempblockvolumetable; protected string m_temphashtable; public LocalBlocklistUpdateDatabase(string path, long blocksize) : base(new LocalDatabase(path, "Rebuild"), blocksize) { } public LocalBlocklistUpdateDatabase(LocalDatabase parentdb, long blocksize) : base(parentdb, blocksize) { } public void FindMissingBlocklistHashes() { m_tempblockvolumetable = "MissingBlocks-" + Library.Utility.Utility.ByteArrayAsHexString(Guid.NewGuid().ToByteArray()); m_temphashtable = "Hashlist-" + Library.Utility.Utility.ByteArrayAsHexString(Guid.NewGuid().ToByteArray()); using (var cmd = m_connection.CreateCommand()) { cmd.ExecuteNonQuery(string.Format(@"CREATE TEMPORARY TABLE ""{0}"" AS SELECT DISTINCT ""Block"".""VolumeID"" AS ""VolumeID"", ""BlocklistHash"".""Hash"" AS ""Hash"", 0 AS ""Restored"" FROM ""Block"", ""BlocklistHash"" WHERE ""Block"".""Hash"" = ""BlocklistHash"".""Hash"" ", m_tempblockvolumetable)); cmd.ExecuteNonQuery(string.Format(@"INSERT INTO ""BlocksetEntry"" (""BlocksetID"", ""Index"", ""BlockID"") SELECT DISTINCT ""Blockset"".""ID"", 0, ""Block"".""ID"" FROM ""Blockset"", ""Block"" WHERE ""Blockset"".""Fullhash"" = ""Block"".""Hash"" AND ""Blockset"".""Length"" < {0} AND ""Blockset"".""ID"" NOT IN (SELECT ""BlocksetID"" FROM ""BlocksetEntry"") ", m_blocksize)); cmd.ExecuteNonQuery(string.Format(@"CREATE TEMPORARY TABLE ""{0}"" (""Hash"" TEXT NOT NULL, ""Index"" INTEGER NOT NULL)", m_temphashtable)); } } public IEnumerable GetBlockLists(long volumeid) { return new BlocklistsEnumerable(m_connection, m_tempblockvolumetable, volumeid); } public void UpdateBlocklist(string hash, IEnumerable hashes, long hashsize, System.Data.IDbTransaction transaction) { using (var cmd = m_connection.CreateCommand()) { cmd.Transaction = transaction; cmd.CommandText = string.Format(@"UPDATE ""{0}"" SET ""Restored"" = 1 WHERE ""Restored"" = 0 AND ""Hash"" = ? ", m_tempblockvolumetable); cmd.AddParameter(hash); var c = cmd.ExecuteNonQuery(); if (c != 1) throw new Exception(string.Format("Blocklist not found {0}", hash)); cmd.ExecuteNonQuery(string.Format(@"DELETE FROM ""{0}""", m_temphashtable)); cmd.CommandText = string.Format(@"INSERT INTO ""{0}"" (""Hash"", ""Index"") VALUES (?, ?)", m_temphashtable); long ix = 0; foreach (var h in hashes) { c = cmd.ExecuteNonQuery(null, h, ix); if (c != 1) throw new Exception(string.Format("Insert row error {0}", h)); ix++; } cmd.CommandText = string.Format(@"INSERT INTO ""BlocksetEntry"" (""BlocksetID"", ""Index"", ""BlockID"") SELECT ""A"".""BlocksetID"", (""A"".""Index"" * ?) + ""B"".""Index"", ""C"".""ID"" FROM ""BlocklistHash"" A, ""{0}"" B, ""Block"" C WHERE ""A"".""Hash"" = ? AND ""C"".""Hash"" = ""B"".""Hash"" ", m_temphashtable); c = cmd.ExecuteNonQuery(null, (m_blocksize / hashsize), hash); if (c == 0 || c % ix != 0) throw new Exception(string.Format("Wrong number of inserts, got {0} records from {1} hashes!", c, ix)); } } public IList GetMissingBlockListVolumes() { using (var cmd = m_connection.CreateCommand()) { List result = new List(); cmd.CommandText = string.Format(@"SELECT DISTINCT ""RemoteVolume"".""Name"", ""RemoteVolume"".""Hash"", ""RemoteVolume"".""Size"" FROM ""RemoteVolume"" WHERE ""ID"" IN (SELECT DISTINCT ""VolumeID"" FROM ""{0}"")", m_tempblockvolumetable); using (var rd = cmd.ExecuteReader()) { object[] r = new object[3]; while (rd.Read()) { rd.GetValues(r); result.Add(new RemoteVolume( r[0] == null ? null : r[0].ToString(), r[1] == null ? null : r[1].ToString(), r[2] == null ? -1 : Convert.ToInt64(r[2]) )); } } return result; } } public override void Dispose() { base.Dispose(); using (var cmd = m_connection.CreateCommand()) { if (m_tempblockvolumetable != null) try { cmd.CommandText = string.Format(@"DROP TABLE ""{0}""", m_tempblockvolumetable); cmd.ExecuteNonQuery(); } finally { m_tempblockvolumetable = null; } if (m_temphashtable != null) try { cmd.CommandText = string.Format(@"DROP TABLE ""{0}""", m_temphashtable); cmd.ExecuteNonQuery(); } finally { m_temphashtable = null; } } } } }