diff --git a/Duplicati/Library/Main/Database/LocalListDatabase.cs b/Duplicati/Library/Main/Database/LocalListDatabase.cs
index d1d852c57..95fb130ac 100644
--- a/Duplicati/Library/Main/Database/LocalListDatabase.cs
+++ b/Duplicati/Library/Main/Database/LocalListDatabase.cs
@@ -1099,45 +1099,59 @@ namespace Duplicati.Library.Main.Database
/// Gets the minimal unique prefix entries for a given fileset ID.
/// This returns the roots of all unique paths in the fileset.
///
- /// The fileset id
- /// The entries that are unique roots
- public IList GetMinimalUniquePrefixEntries(long filesetId)
+ /// The fileset id.
+ /// A cancellation token to cancel the operation.
+ /// A task that when awaited returns a list of the entries that are unique roots.
+ public async IAsyncEnumerable GetMinimalUniquePrefixEntries(long filesetId, [EnumeratorCancellation] CancellationToken token)
{
- var results = new List();
-
- using var cmd = m_connection.CreateCommand();
+ await using var cmd = m_connection.CreateCommand();
cmd.SetCommandAndParameters(@"
WITH AllPaths AS (
- SELECT fl.""ID"" AS ""FileID"",
+ SELECT
+ fl.""ID"" AS ""FileID"",
pp.""Prefix"" || fl.""Path"" AS ""FullPath"",
fl.""BlocksetID"",
b.""Length"",
fe.""Lastmodified""
FROM ""FilesetEntry"" fe
- JOIN ""FileLookup"" fl ON fe.""FileID"" = fl.""ID""
- JOIN ""PathPrefix"" pp ON fl.""PrefixID"" = pp.""ID""
- LEFT JOIN ""Blockset"" b ON fl.""BlocksetID"" = b.""ID""
+ JOIN ""FileLookup"" fl
+ ON fe.""FileID"" = fl.""ID""
+ JOIN ""PathPrefix"" pp
+ ON fl.""PrefixID"" = pp.""ID""
+ LEFT JOIN ""Blockset"" b
+ ON fl.""BlocksetID"" = b.""ID""
WHERE fe.""FilesetID"" = @FilesetId
),
RootCandidates AS (
SELECT a1.*
FROM AllPaths a1
WHERE NOT EXISTS (
- SELECT 1 FROM AllPaths a2
- WHERE a2.""FullPath"" != a1.""FullPath""
- AND a1.""FullPath"" LIKE a2.""FullPath"" || '%'
+ SELECT 1
+ FROM AllPaths a2
+ WHERE
+ a2.""FullPath"" != a1.""FullPath""
+ AND a1.""FullPath"" LIKE a2.""FullPath"" || '%'
)
)
SELECT ""FullPath"", ""Length"",
- CASE WHEN ""BlocksetID"" = -100 THEN 1 ELSE 0 END AS ""IsDirectory"",
- CASE WHEN ""BlocksetID"" = -200 THEN 1 ELSE 0 END AS ""IsSymlink"",
+ CASE
+ WHEN ""BlocksetID"" = -100
+ THEN 1
+ ELSE 0
+ END AS ""IsDirectory"",
+ CASE
+ WHEN ""BlocksetID"" = -200
+ THEN 1
+ ELSE 0
+ END AS ""IsSymlink"",
""Lastmodified""
FROM RootCandidates
ORDER BY ""FullPath""
- ");
- cmd.SetParameterValue("@FilesetId", filesetId);
+ ")
+ .SetTransaction(m_rtr)
+ .SetParameterValue("@FilesetId", filesetId);
- foreach (var rd in cmd.ExecuteReaderEnumerable())
+ await foreach (var rd in cmd.ExecuteReaderEnumerableAsync(token))
{
var path = rd.ConvertValueToString(0) ?? string.Empty;
var size = rd.ConvertValueToInt64(1, -1);
@@ -1145,10 +1159,8 @@ namespace Duplicati.Library.Main.Database
var isSymlink = rd.GetInt32(3) != 0;
var lastModified = new DateTime(rd.ConvertValueToInt64(4, 0), DateTimeKind.Utc);
- results.Add(new FolderEntry(path, size, isDir, isSymlink, lastModified));
+ yield return new FolderEntry(path, size, isDir, isSymlink, lastModified);
}
-
- return results;
}
///
diff --git a/Duplicati/Library/Main/Operation/ListFolderHandler.cs b/Duplicati/Library/Main/Operation/ListFolderHandler.cs
index 8c7aaf569..0305812f2 100644
--- a/Duplicati/Library/Main/Operation/ListFolderHandler.cs
+++ b/Duplicati/Library/Main/Operation/ListFolderHandler.cs
@@ -63,7 +63,10 @@ internal static class ListFolderHandler
{
if (folders != null && folders.Length > 1)
throw new UserInformationException("When no folder is specified, only one folder can be listed", "MultipleFoldersFound");
- var rootFolders = db.GetMinimalUniquePrefixEntries(filesetIds[0]);
+ var rootFolders = await db
+ .GetMinimalUniquePrefixEntries(filesetIds[0], result.TaskControl.ProgressToken)
+ .ToListAsync(cancellationToken: result.TaskControl.ProgressToken)
+ .ConfigureAwait(false);
result.Entries = new PaginatedResults(0, rootFolders.Count, 1, rootFolders.Count, rootFolders);
}
else
diff --git a/Duplicati/Library/Main/Operation/TestHandler.cs b/Duplicati/Library/Main/Operation/TestHandler.cs
index 033bc459f..b8b48581d 100644
--- a/Duplicati/Library/Main/Operation/TestHandler.cs
+++ b/Duplicati/Library/Main/Operation/TestHandler.cs
@@ -433,9 +433,9 @@ namespace Duplicati.Library.Main.Operation
await backendManager.DeleteAsync(vol.Name, vol.Size, true, m_result.TaskControl.ProgressToken).ConfigureAwait(false);
await backendManager.WaitForEmptyAsync(repairdb, m_result.TaskControl.ProgressToken).ConfigureAwait(false);
await repairdb.Transaction
- .CommitAsync("ReplaceFaultyIndexFileCommit")
+ .CommitAsync("ReplaceFaultyIndexFileCommit", token: cancellationToken)
.ConfigureAwait(false);
- m_results.RemoveResult(vol.Name);
+ m_result.RemoveResult(vol.Name);
}
}
catch (Exception ex)
diff --git a/Duplicati/UnitTest/DirectListHandlerTests.cs b/Duplicati/UnitTest/DirectListHandlerTests.cs
index 39994f8c9..4da12acab 100644
--- a/Duplicati/UnitTest/DirectListHandlerTests.cs
+++ b/Duplicati/UnitTest/DirectListHandlerTests.cs
@@ -337,7 +337,8 @@ namespace Duplicati.UnitTest
]);
var result = await db
- .GetMinimalUniquePrefixEntries(1).Select(e => e.Path, CancellationToken.None)
+ .GetMinimalUniquePrefixEntries(1, CancellationToken.None)
+ .Select(e => e.Path)
.ToListAsync()
.ConfigureAwait(false);
@@ -363,7 +364,8 @@ namespace Duplicati.UnitTest
]);
var result = await db
- .GetMinimalUniquePrefixEntries(1).Select(e => e.Path, CancellationToken.None)
+ .GetMinimalUniquePrefixEntries(1, CancellationToken.None)
+ .Select(e => e.Path)
.ToListAsync()
.ConfigureAwait(false);
@@ -387,7 +389,8 @@ namespace Duplicati.UnitTest
]);
var result = await db
- .GetMinimalUniquePrefixEntries(1).Select(e => e.Path, CancellationToken.None)
+ .GetMinimalUniquePrefixEntries(1, CancellationToken.None)
+ .Select(e => e.Path)
.ToListAsync()
.ConfigureAwait(false);
@@ -414,7 +417,8 @@ namespace Duplicati.UnitTest
]);
var result = await db
- .GetMinimalUniquePrefixEntries(1).Select(e => e.Path, CancellationToken.None)
+ .GetMinimalUniquePrefixEntries(1, CancellationToken.None)
+ .Select(e => e.Path)
.ToListAsync()
.ConfigureAwait(false);
@@ -429,7 +433,7 @@ namespace Duplicati.UnitTest
}
[Test]
- public void GetMinimalUniquePrefixEntries_ShouldReturnExpectedMinimalRoots()
+ public async Task GetMinimalUniquePrefixEntries_ShouldReturnExpectedMinimalRoots()
{
// Arrange: Prepare prefixes (minimal unique) and contents
var testPrefixes = new[]
@@ -448,10 +452,15 @@ namespace Duplicati.UnitTest
// Act
using var tempFile = new TempFile();
- using var db = new LocalListDatabase(tempFile, 1);
+ await using var db = await LocalListDatabase.CreateAsync(tempFile, null, CancellationToken.None)
+ .ConfigureAwait(false);
SeedTestData(db, testPrefixes);
- var resultItems = db.GetMinimalUniquePrefixEntries(1).ToList();
+ var resultItems = await db
+ .GetMinimalUniquePrefixEntries(1, CancellationToken.None)
+ .ToListAsync()
+ .ConfigureAwait(false);
+
var result = resultItems.Select(e => e.Path).ToList();
Assert.That(result, Does.Contain(@"C:\Downloads\testsource\AA\"));