From 7bc6c2b6ff8cbc2c7ea747d57875ed5addf993bc Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Fri, 15 Mar 2013 21:16:29 +0100 Subject: [PATCH] Rewrote the way the restore operation uses file lists for partial restore --- .../Database/LocalRestoreDatabase.cs | 52 +++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/Duplicati/Library/Main/ForestHash/Database/LocalRestoreDatabase.cs b/Duplicati/Library/Main/ForestHash/Database/LocalRestoreDatabase.cs index 7ba62668b..e8383a733 100644 --- a/Duplicati/Library/Main/ForestHash/Database/LocalRestoreDatabase.cs +++ b/Duplicati/Library/Main/ForestHash/Database/LocalRestoreDatabase.cs @@ -24,7 +24,7 @@ namespace Duplicati.Library.Main.ForestHash.Database m_blocksize = blocksize; } - public void PrepareRestoreFilelist(DateTime restoretime, string[] p, Utility.FilenameFilter filenameFilter) + public void PrepareRestoreFilelist(DateTime restoretime, string[] p, Utility.FilenameFilter filenameFilter, CommunicationStatistics stat) { var guid = Utility.Utility.ByteArrayAsHexString(Guid.NewGuid().ToByteArray()); @@ -50,22 +50,44 @@ namespace Duplicati.Library.Main.ForestHash.Database } else if (p != null && p.Length > 0) { - // Restore files in list - cmd.CommandText = string.Format(@"INSERT INTO ""{0}"" (""Path"", ""BlocksetID"", ""MetadataID"") SELECT ""Fileset"".""Path"", ""Fileset"".""BlocksetID"", ""Fileset"".""MetadataID"" FROM ""Fileset"", ""OperationFileset"" WHERE ""Fileset"".""ID"" = ""OperationFileset"".""FilesetID"" AND ""OperationID"" = ? AND ""Path"" = ? ", m_tempfiletable); - cmd.AddParameter(operationID); - cmd.AddParameter(); - - //TODO: Actually faster to just create a temp table with files, - // and then using a select to create the temporary table - // but there should be some way to report if filenames - // are not found - - foreach (var s in p) + using(var tr = m_connection.BeginTransaction()) { - cmd.SetParameterValue(1, s); + var m_filenamestable = "Filenames-" + guid; + cmd.Transaction = tr; + cmd.ExecuteNonQuery(string.Format(@"CREATE TEMPORARY TABLE ""{0}"" (""Path"" TEXT NOT NULL) ", m_filenamestable)); + cmd.CommandText = string.Format(@"INSERT INTO ""{0}"" (""Path"") VALUES (?)", m_filenamestable); + cmd.AddParameter(); + + foreach (var s in p) + { + cmd.SetParameterValue(0, s); + cmd.ExecuteNonQuery(); + } + + //TODO: Handle case-insensitive filename lookup + cmd.CommandText = string.Format(@"INSERT INTO ""{0}"" (""Path"", ""BlocksetID"", ""MetadataID"") SELECT ""Fileset"".""Path"", ""Fileset"".""BlocksetID"", ""Fileset"".""MetadataID"" FROM ""Fileset"", ""OperationFileset"" WHERE ""Fileset"".""ID"" = ""OperationFileset"".""FilesetID"" AND ""OperationID"" = ? AND ""Path"" IN (SELECT DISTINCT ""Path"" FROM ""{1}"") ", m_tempfiletable, m_filenamestable); + cmd.SetParameterValue(0, operationID); var c = cmd.ExecuteNonQuery(); - if (c != 1) - throw new Exception("Failed to insert file into temp table"); + + cmd.Parameters.Clear(); + + if (c != p.Length) + { + var sb = new StringBuilder(); + sb.AppendLine(); + + using(var rd = cmd.ExecuteReader(string.Format(@"SELECT ""Path"" FROM ""{0}"" WHERE ""Path"" NOT IN (SELECT ""Path"" FROM ""{1}"")", m_filenamestable, m_tempfiletable))) + while(rd.Read()) + sb.AppendLine(rd.GetValue(0).ToString()); + + DateTime actualrestoretime = Convert.ToDateTime(cmd.ExecuteScalar(@"SELECT ""Timestamp"" FROM ""Operation"" WHERE ""ID"" = ?", operationID)); + stat.LogWarning(string.Format("{0} File(s) were not found in list of files for backup at {1}, will not be restored: {2}", p.Length - c, actualrestoretime.ToLocalTime(), sb), null); + cmd.Parameters.Clear(); + } + + cmd.ExecuteNonQuery(string.Format(@"DROP TABLE ""{0}"" ", m_filenamestable)); + + tr.Commit(); } } else