diff --git a/Duplicati/Library/Main/Database/LocalBackupDatabase.cs b/Duplicati/Library/Main/Database/LocalBackupDatabase.cs
index 596195dfa..62fd954d4 100644
--- a/Duplicati/Library/Main/Database/LocalBackupDatabase.cs
+++ b/Duplicati/Library/Main/Database/LocalBackupDatabase.cs
@@ -748,6 +748,7 @@ namespace Duplicati.Library.Main.Database
///
/// The block key.
/// The size of the block.
+ /// The ID of the volume to which the block belongs.
/// A taskt that when awaited contains true if the block should be added to the current output.
public async Task AddBlock(string key, long size, long volumeid)
{
@@ -828,7 +829,7 @@ namespace Duplicati.Library.Main.Database
/// The size of the blockset.
/// The size of the blocks in the blockset.
/// The list of hashes.
- /// The id of the blockset, new or old.
+ /// The list of hashes for the blocklist, or null if no blocklist is used.
/// A task that when awaited contains a tuple with the first value indicating whether the blockset was created, and the second value being the blockset ID.
public async Task<(bool, long)> AddBlockset(string filehash, long size, int blocksize, IEnumerable hashes, IEnumerable blocklistHashes)
{
@@ -954,14 +955,12 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Adds a metadata set to the database, and returns a value indicating if the record was new
+ /// Adds a metadata set to the database, and returns a tuple indicating if the record was new and the ID of the metadata set.
///
- /// The metadata hash
- /// The size of the metadata
- /// The transaction to execute under
- /// The id of the blockset to add
- /// The id of the metadata set
- /// True if the set was added to the database, false otherwise
+ /// The metadata hash.
+ /// The size of the metadata.
+ /// The id of the blockset to add.
+ /// A task that when awaited contains a tuple with the first value indicating if the metadata set was added, and the second value being the metadata ID.
public async Task<(bool, long)> AddMetadataset(string filehash, long size, long blocksetid)
{
var (metadatafound, metadataid) = await GetMetadatasetID(filehash, size)
@@ -981,14 +980,14 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Adds a file record to the database
+ /// Adds a file record to the database.
///
- /// The path prefix ID
- /// The path to the file
- /// The time the file was modified
- /// The ID of the hashkey for the file
- /// The ID for the metadata
- /// The transaction to use for insertion, or null for no transaction
+ /// The path prefix ID.
+ /// The path to the file.
+ /// The time the file was modified.
+ /// The ID of the hashkey for the file.
+ /// The ID for the metadata.
+ /// A task that completes when the file is added.
public async Task AddFile(long pathprefixid, string filename, DateTime lastmodified, long blocksetID, long metadataID)
{
var fileidobj = await m_findfilesetCommand
@@ -1017,13 +1016,13 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Adds a file record to the database
+ /// Adds a file record to the database.
///
- /// The path to the file
- /// The time the file was modified
- /// The ID of the hashkey for the file
- /// The ID for the metadata
- /// The transaction to use for insertion, or null for no transaction
+ /// The path to the file.
+ /// The time the file was modified.
+ /// The ID of the hashkey for the file.
+ /// The ID for the metadata.
+ /// A task that completes when the file is added.
public async Task AddFile(string filename, DateTime lastmodified, long blocksetID, long metadataID)
{
var split = SplitIntoPrefixAndName(filename);
@@ -1039,11 +1038,11 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Adds a known file to the fileset
+ /// Adds a known file to the fileset.
///
- /// Id of the file
- /// The time the file was modified
- /// The transaction to use for insertion, or null for no transaction
+ /// Id of the file.
+ /// The time the file was modified.
+ /// A task that completes when the file is added.
public async Task AddKnownFile(long fileid, DateTime lastmodified)
{
await m_insertfileOperationCommand
@@ -1055,18 +1054,40 @@ namespace Duplicati.Library.Main.Database
.ConfigureAwait(false);
}
+ ///
+ /// Adds a directory entry to the fileset.
+ ///
+ /// The path to the directory.
+ /// The ID for the metadata.
+ /// The time the directory was modified.
+ /// A task that completes when the directory entry is added.
public async Task AddDirectoryEntry(string path, long metadataID, DateTime lastmodified)
{
await AddFile(path, lastmodified, FOLDER_BLOCKSET_ID, metadataID)
.ConfigureAwait(false);
}
+ ///
+ /// Adds a symlink entry to the fileset.
+ ///
+ /// The path to the symlink.
+ /// The ID for the metadata.
+ /// The time the symlink was modified.
+ /// A task that completes when the symlink entry is added.
public async Task AddSymlinkEntry(string path, long metadataID, DateTime lastmodified)
{
await AddFile(path, lastmodified, SYMLINK_BLOCKSET_ID, metadataID)
.ConfigureAwait(false);
}
+ ///
+ /// Gets the ID, last modified time and size of a file in the fileset.
+ ///
+ /// The ID of the path prefix.
+ /// The path to the file.
+ /// The ID of the fileset.
+ /// Whether to include the file length in the result.
+ /// A task that when awaited contains a tuple with the file ID, last modified time, and file length.
public async Task<(long, DateTime, long)> GetFileLastModified(long prefixid, string path, long filesetid, bool includeLength)
{
DateTime oldModified;
@@ -1155,6 +1176,11 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Gets the metadata hash and size for a file.
+ ///
+ /// The ID of the file.
+ /// A task that when awaited contains a tuple with the metadata hash and size, or null if the file does not exist.
public async Task<(string MetadataHash, long Size)?> GetMetadataHashAndSizeForFile(long fileid)
{
m_selectfilemetadatahashandsizeCommand
@@ -1171,7 +1197,11 @@ namespace Duplicati.Library.Main.Database
return null;
}
-
+ ///
+ /// Gets the hash of a file.
+ ///
+ /// The ID of the file.
+ /// A task that when awaited contains the hash of the file, or null if the file does not exist.
public async Task GetFileHash(long fileid)
{
var r = await m_selectfileHashCommand
@@ -1209,6 +1239,10 @@ namespace Duplicati.Library.Main.Database
await base.DisposeAsync().ConfigureAwait(false);
}
+ ///
+ /// Gets the size of the last written DBlock volume.
+ ///
+ /// A task that when awaited contains the size of the last written DBlock volume, or -1 if no such volume exists.
public async Task GetLastWrittenDBlockVolumeSize()
{
using (var cmd = m_connection.CreateCommand(m_rtr))
@@ -1227,12 +1261,24 @@ namespace Duplicati.Library.Main.Database
.ConfigureAwait(false);
}
+ ///
+ /// Gets the ID of the previous fileset based on the operation timestamp and current fileset ID.
+ ///
+ /// The command to use for the query.
+ /// A task that when awaited contains the ID of the previous fileset, or -1 if no such fileset exists.
private async Task GetPreviousFilesetID(SqliteCommand cmd)
{
return await GetPreviousFilesetID(cmd, OperationTimestamp, m_filesetId)
.ConfigureAwait(false);
}
+ ///
+ /// Gets the ID of the previous fileset based on the operation timestamp and current fileset ID.
+ ///
+ /// The command to use for the query.
+ /// The timestamp to use for the query.
+ /// The current fileset ID.
+ /// A task that when awaited contains the ID of the previous fileset, or -1 if no such fileset exists.
private async Task GetPreviousFilesetID(SqliteCommand cmd, DateTime timestamp, long filesetid)
{
return await cmd
@@ -1251,6 +1297,10 @@ namespace Duplicati.Library.Main.Database
.ConfigureAwait(false);
}
+ ///
+ /// Gets the count and size of files in the last backup fileset.
+ ///
+ /// A task that when awaited contains a tuple with the count of files and the total size of files in the last backup fileset.
internal async Task> GetLastBackupFileCountAndSize()
{
using (var cmd = m_connection.CreateCommand(m_rtr))
@@ -1306,6 +1356,11 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Updates the change statistics for the current fileset based on the results of a backup operation.
+ ///
+ /// The results of the backup operation.
+ /// A task that completes when the change statistics are updated.
internal async Task UpdateChangeStatistics(BackupResults results)
{
using (var cmd = m_connection.CreateCommand(m_rtr))
@@ -1321,8 +1376,8 @@ namespace Duplicati.Library.Main.Database
/// Populates FilesetEntry table with files from previous fileset, which aren't
/// yet part of the new fileset, and which aren't on the (optional) list of deleted paths.
///
- /// Transaction
- /// List of deleted paths, or null
+ /// List of deleted paths, or null.
+ /// A task that completes when the files are appended.
public async Task AppendFilesFromPreviousSet(IEnumerable? deleted = null)
{
await AppendFilesFromPreviousSet(deleted, m_filesetId, -1, OperationTimestamp)
@@ -1333,11 +1388,11 @@ namespace Duplicati.Library.Main.Database
/// Populates FilesetEntry table with files from previous fileset, which aren't
/// yet part of the new fileset, and which aren't on the (optional) list of deleted paths.
///
- /// Transaction
- /// List of deleted paths, or null
- /// Current file-set ID
- /// Source file-set ID
- /// If filesetid == -1, used to locate previous file-set
+ /// List of deleted paths, or null.
+ /// Current file-set ID.
+ /// Source file-set ID.
+ /// If filesetid == -1, used to locate previous file-set.
+ /// A task that completes when the files are appended.
public async Task AppendFilesFromPreviousSet(IEnumerable? deleted, long filesetid, long prevId, DateTime timestamp)
{
using (var cmd = m_connection.CreateCommand())
@@ -1415,8 +1470,8 @@ namespace Duplicati.Library.Main.Database
/// yet part of the new fileset, and which aren't excluded by the (optional) exclusion
/// predicate.
///
- /// Transaction
- /// Optional exclusion predicate (true = exclude file)
+ /// Optional exclusion predicate (true = exclude file).
+ /// A task that completes when the files are appended.
public async Task AppendFilesFromPreviousSetWithPredicate(Func exclusionPredicate)
{
await AppendFilesFromPreviousSetWithPredicate(exclusionPredicate, m_filesetId, -1, OperationTimestamp)
@@ -1428,11 +1483,11 @@ namespace Duplicati.Library.Main.Database
/// yet part of the new fileset, and which aren't excluded by the (optional) exclusion
/// predicate.
///
- /// Transaction
/// Optional exclusion predicate (true = exclude file)
/// Current fileset ID
/// Source fileset ID
/// If prevFileSetId == -1, used to locate previous fileset
+ /// A task that completes when the files are appended.
public async Task AppendFilesFromPreviousSetWithPredicate(Func exclusionPredicate, long fileSetId, long prevFileSetId, DateTime timestamp)
{
if (exclusionPredicate == null)
@@ -1542,15 +1597,20 @@ namespace Duplicati.Library.Main.Database
///
/// Creates a timestamped backup operation to correctly associate the fileset with the time it was created.
///
- /// The ID of the fileset volume to update
- /// The timestamp of the operation to create
- /// An optional external transaction
+ /// The ID of the fileset volume to update.
+ /// The timestamp of the operation to create.
+ /// A task that when awaited contains the ID of the created fileset.
public override async Task CreateFileset(long volumeid, DateTime timestamp)
{
return m_filesetId = await base.CreateFileset(volumeid, timestamp)
.ConfigureAwait(false);
}
+ ///
+ /// Retrieves the names of temporary fileset volumes that are incomplete.
+ ///
+ /// If true, only the latest incomplete fileset volume will be returned.
+ /// A task that when awaited contains a list of volume names.
public async Task> GetTemporaryFilelistVolumeNames(bool latestOnly)
{
var incompleteFilesetIDs = GetIncompleteFilesets().OrderBy(x => x.Value).Select(x => x.Key);
@@ -1574,6 +1634,10 @@ namespace Duplicati.Library.Main.Database
return volumeNames;
}
+ ///
+ /// Retrieves the names of remote volumes that are missing index files.
+ ///
+ /// An asynchronous enumerable of volume names that are missing index files.
public async IAsyncEnumerable GetMissingIndexFiles()
{
using var cmd = m_connection.CreateCommand(m_rtr)
@@ -1599,6 +1663,14 @@ namespace Duplicati.Library.Main.Database
yield return rd.ConvertValueToString(0) ?? throw new Exception("Unexpected null value for volume name");
}
+ ///
+ /// Moves a block from one volume to another.
+ ///
+ /// The hash of the block to move.
+ /// The size of the block to move.
+ /// The ID of the source volume.
+ /// The ID of the target volume.
+ /// A task that completes when the block is moved.
public async Task MoveBlockToVolume(string blockkey, long size, long sourcevolumeid, long targetvolumeid)
{
using (var cmd = m_connection.CreateCommand(m_rtr))
@@ -1623,6 +1695,13 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Safely deletes a remote volume by checking if it has any associated blocks.
+ /// If it does, an exception is thrown; otherwise, the volume is removed.
+ ///
+ /// The name of the remote volume to delete.
+ /// A task that completes when the remote volume is safely deleted.
+ /// Thrown if the volume has associated blocks.
public async Task SafeDeleteRemoteVolume(string name)
{
var volumeid = await GetRemoteVolumeID(name).ConfigureAwait(false);
@@ -1645,6 +1724,11 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Retrieves the hashes of blocks that are on the blocklist for a given volume.
+ ///
+ /// The name of the volume to check.
+ /// A task that when awaited contains an array of blocklist hashes.
public async Task GetBlocklistHashes(string name)
{
var volumeid = GetRemoteVolumeID(name);
@@ -1670,6 +1754,10 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Retrieves the first path in the database, ordered by length in descending order.
+ ///
+ /// A task that when awaited contains the first path, or null if no paths exist.
public async Task GetFirstPath()
{
using (var cmd = m_connection.CreateCommand(m_rtr))
@@ -1690,9 +1778,10 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Retrieves change journal data for file set
+ /// Retrieves the change journal data for file set.
///
- /// Fileset-ID
+ /// The Fileset-ID.
+ /// An asynchronous enumerable of USN journal data entries.
public async IAsyncEnumerable GetChangeJournalData(long fileSetId)
{
var data = new List();
@@ -1725,10 +1814,11 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Adds NTFS change journal data for file set and volume
+ /// Adds NTFS change journal data for file set and volume.
///
- /// Data to add
- /// An optional external transaction
+ /// Data to add.
+ /// A task that completes when the data is added.
+ /// Thrown if unable to add change journal entry.
public async Task CreateChangeJournalData(IEnumerable data)
{
foreach (var entry in data)
@@ -1768,11 +1858,11 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Adds NTFS change journal data for file set and volume
+ /// Adds NTFS change journal data for file set and volume.
///
- /// Data to add
- /// Existing file set to update
- /// An optional external transaction
+ /// Data to add.
+ /// Existing file set to update.
+ /// A task that completes when the data is added.
public async Task UpdateChangeJournalData(IEnumerable data, long fileSetId)
{
foreach (var entry in data)
@@ -1801,11 +1891,10 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Checks if a blocklist hash is known
+ /// Checks if a blocklist hash is known.
///
- /// The hash to check
- /// An optional external transaction
- /// True if the hash is known, false otherwise
+ /// The hash to check.
+ /// A task that when awaited returns true if the hash is known, false otherwise.
public async Task IsBlocklistHashKnown(string hash)
{
var res = await m_getfirstfilesetwithblockinblockset
diff --git a/Duplicati/Library/Main/Database/LocalBugReportDatabase.cs b/Duplicati/Library/Main/Database/LocalBugReportDatabase.cs
index 6470afa09..3b6674488 100644
--- a/Duplicati/Library/Main/Database/LocalBugReportDatabase.cs
+++ b/Duplicati/Library/Main/Database/LocalBugReportDatabase.cs
@@ -27,6 +27,10 @@ using Duplicati.Library.Common.IO;
namespace Duplicati.Library.Main.Database
{
+
+ ///
+ /// A local database for bug reports, which obfuscates sensitive data before generating a bug report.
+ ///
internal class LocalBugReportDatabase : LocalDatabase
{
///
@@ -34,6 +38,13 @@ namespace Duplicati.Library.Main.Database
///
private static readonly string LOGTAG = Logging.Log.LogTagFromType(typeof(LocalBugReportDatabase));
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The path to the database file.
+ /// The size of the page cache.
+ /// An optional existing database instance to use. Used to mimic constructor chaining.
+ /// A task that when awaited contains a new instance of .
public static async Task CreateAsync(string path, long pagecachesize, LocalBugReportDatabase? dbnew = null)
{
dbnew ??= new LocalBugReportDatabase();
@@ -46,6 +57,10 @@ namespace Duplicati.Library.Main.Database
return dbnew;
}
+ ///
+ /// Obfuscates sensitive data in the database, readying it for a bug report.
+ ///
+ /// A task that completes when the obfuscation is finished.
public async Task Fix()
{
using (var cmd = m_connection.CreateCommand(m_rtr))
diff --git a/Duplicati/Library/Main/Database/LocalDatabase.cs b/Duplicati/Library/Main/Database/LocalDatabase.cs
index b78522f23..4f0cc920b 100644
--- a/Duplicati/Library/Main/Database/LocalDatabase.cs
+++ b/Duplicati/Library/Main/Database/LocalDatabase.cs
@@ -40,7 +40,12 @@ using System.Threading;
namespace Duplicati.Library.Main.Database
{
- internal class LocalDatabase : IDisposable
+ ///
+ /// Represents a local database for Duplicati operations.
+ /// This class provides methods to interact with the local SQLite database, including
+ /// managing remote volumes, logging operations, and handling transactions.
+ ///
+ internal class LocalDatabase : IDisposable, IAsyncDisposable
{
///
/// The tag used for logging
@@ -53,39 +58,114 @@ namespace Duplicati.Library.Main.Database
/// SQLite has a limit of 999 parameters in a single statement
public const int CHUNK_SIZE = 128;
- // All of the required fields have been set to null! to ignore the compiler warning, as they will be initialized in the Create* factory functions.
+ // All of the required fields have been set to null! to ignore the compiler warning, as they will be initialized in the Create* factory methods.
+ ///
+ /// The SQLite connection to the local database.
+ ///
protected SqliteConnection m_connection = null!;
+ ///
+ /// The operation ID for the current operation.
+ ///
protected long m_operationid = -1;
+ ///
+ /// The size of the SQLite page cache.
+ ///
protected long m_pagecachesize;
+ ///
+ /// Indicates whether the database has executed a vacuum operation.
+ ///
private bool m_hasExecutedVacuum;
+ ///
+ /// The reusable transaction for the current operation.
+ ///
protected ReusableTransaction m_rtr = null!;
+ ///
+ /// A read-only property that provides access to the current transaction.
+ ///
public ReusableTransaction Transaction { get { return m_rtr; } }
+ ///
+ /// The command used to update a remote volume in the database.
+ ///
private SqliteCommand m_updateremotevolumeCommand = null!;
+ ///
+ /// The command used to select remote volumes from the database.
+ ///
private SqliteCommand m_selectremotevolumesCommand = null!;
+ ///
+ /// The command used to select a specific remote volume by name.
+ ///
private SqliteCommand m_selectremotevolumeCommand = null!;
+ ///
+ /// The command used to remove a remote volume from the database.
+ ///
private SqliteCommand m_removeremotevolumeCommand = null!;
+ ///
+ /// The command used to remove deleted remote volumes from the database.
+ ///
private SqliteCommand m_removedeletedremotevolumeCommand = null!;
+ ///
+ /// The command used to select the ID of a remote volume by name.
+ ///
private SqliteCommand m_selectremotevolumeIdCommand = null!;
+ ///
+ /// The command used to create a new remote volume in the database.
+ ///
private SqliteCommand m_createremotevolumeCommand = null!;
+ ///
+ /// The command used to select duplicate remote volumes from the database.
+ ///
private SqliteCommand m_selectduplicateRemoteVolumesCommand = null!;
+ ///
+ /// The command used to insert log data into the database.
+ ///
private SqliteCommand m_insertlogCommand = null!;
+ ///
+ /// The command used to insert remote operation logs into the database.
+ ///
private SqliteCommand m_insertremotelogCommand = null!;
+ ///
+ /// The command used to insert index block links into the database.
+ ///
private SqliteCommand m_insertIndexBlockLink = null!;
+ ///
+ /// The command used to find a path prefix in the database.
+ ///
private SqliteCommand m_findpathprefixCommand = null!;
+ ///
+ /// The command used to insert a new path prefix into the database.
+ ///
private SqliteCommand m_insertpathprefixCommand = null!;
+ ///
+ /// A constant representing the blockset ID for a folder.
+ ///
public const long FOLDER_BLOCKSET_ID = -100;
+ ///
+ /// A constant representing the blockset ID for a symlink.
+ ///
public const long SYMLINK_BLOCKSET_ID = -200;
+ ///
+ /// The timestamp of the operation being performed.
+ ///
public DateTime OperationTimestamp { get; private set; }
+ ///
+ /// A read-only property that provides access to the internal SQLite connection.
+ ///
internal SqliteConnection Connection { get { return m_connection; } }
+ ///
+ /// Indicates whether the database has been disposed.
+ ///
public bool IsDisposed { get; private set; }
+ ///
+ /// Indicates whether the connection should be closed when the database is disposed.
+ ///
public bool ShouldCloseConnection { get; set; }
// Constructor is private to force use of CreateLocalDatabaseAsync
@@ -98,6 +178,13 @@ namespace Duplicati.Library.Main.Database
throw new NotImplementedException("Use the CreateLocalDatabaseAsync or CreateLocalDatabase functions instead");
}
+ ///
+ /// Creates a new SQLite connection to the specified database path with the given page cache size.
+ /// This method ensures that the directory for the database exists and upgrades the database schema if necessary.
+ ///
+ /// The path to the SQLite database file.
+ /// The size of the SQLite page cache in bytes.
+ /// A task that, when awaited, returns a new to the specified database.
protected static async Task CreateConnectionAsync(string path, long pagecachesize)
{
path = Path.GetFullPath(path);
@@ -121,6 +208,16 @@ namespace Duplicati.Library.Main.Database
return c;
}
+ ///
+ /// Creates a new instance of with the specified parameters.
+ /// This method initializes the database connection, sets the operation timestamp, and prepares the necessary commands for database operations.
+ ///
+ /// The path to the SQLite database file.
+ /// The description of the operation being performed.
+ /// Indicates whether the connection should be closed when the database is disposed.
+ /// The size of the SQLite page cache in bytes.
+ /// An optional existing instance to use. If not provided, a new instance will be created.
+ /// A task that, when awaited, returns a new instance of .
public static async Task CreateLocalDatabaseAsync(string path, string operation, bool shouldclose, long pagecachesize, LocalDatabase? db = null)
{
db ??= new LocalDatabase();
@@ -136,6 +233,12 @@ namespace Duplicati.Library.Main.Database
return db;
}
+ ///
+ /// Creates a new instance of based on an existing database instance.
+ /// This method copies the connection and transaction from the parent database and initializes the new database with the same operation timestamp and ID.
+ ///
+ /// The parent instance to copy from.
+ /// An optional existing instance to use. If not provided, a new instance will be created.
public static async Task CreateLocalDatabaseAsync(LocalDatabase dbparent, LocalDatabase? dbnew = null)
{
dbnew ??= new LocalDatabase();
@@ -152,6 +255,14 @@ namespace Duplicati.Library.Main.Database
return dbnew;
}
+ ///
+ /// Creates a new instance of with the specified SQLite connection and operation description.
+ /// This method initializes the database connection, sets the operation timestamp, and prepares the necessary commands for database operations.
+ ///
+ /// The SQLite connection to use for the database operations.
+ /// The description of the operation being performed.
+ /// An optional existing instance to use. If not provided, a new instance will be created.
+ /// A task that, when awaited, returns a new instance of .
public static async Task CreateLocalDatabaseAsync(SqliteConnection connection, string operation, LocalDatabase? dbnew = null)
{
dbnew ??= new LocalDatabase();
@@ -206,6 +317,13 @@ namespace Duplicati.Library.Main.Database
return dbnew;
}
+ ///
+ /// Creates a new instance of with the specified SQLite connection.
+ /// This method initializes the database connection, sets up the reusable transaction, and prepares the necessary commands for database operations.
+ ///
+ /// The SQLite connection to use for the database operations.
+ /// An optional existing instance to use. If not provided, a new instance will be created.
+ /// A task that, when awaited, returns a new instance of .
private static async Task CreateLocalDatabaseAsync(SqliteConnection connection, LocalDatabase? dbnew = null)
{
dbnew ??= new LocalDatabase();
@@ -382,25 +500,55 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Creates a DateTime instance by adding the specified number of seconds to the EPOCH value
+ /// Creates a DateTime instance by adding the specified number of seconds to the EPOCH value.
///
+ /// The number of seconds since the EPOCH (January 1, 1970).
+ /// A DateTime instance representing the specified number of seconds since the EPOCH.
public static DateTime ParseFromEpochSeconds(long seconds)
{
return Library.Utility.Utility.EPOCH.AddSeconds(seconds);
}
+ ///
+ /// Updates the state of a remote volume in the database.
+ ///
+ /// The name of the remote volume to update.
+ /// The new state of the remote volume.
+ /// The size of the remote volume in bytes.
+ /// The hash of the remote volume, or null if not applicable.
+ /// A task that completes when the remote volume has been updated.
public async Task UpdateRemoteVolume(string name, RemoteVolumeState state, long size, string? hash)
{
await UpdateRemoteVolume(name, state, size, hash, false)
.ConfigureAwait(false);
}
+ ///
+ /// Updates the state of a remote volume in the database.
+ ///
+ /// The name of the remote volume to update.
+ /// The new state of the remote volume.
+ /// The size of the remote volume in bytes.
+ /// The hash of the remote volume, or null if not applicable.
+ /// If true, suppresses cleanup of the remote volume after updating.
+ /// A task that completes when the remote volume has been updated.
public async Task UpdateRemoteVolume(string name, RemoteVolumeState state, long size, string? hash, bool suppressCleanup)
{
await UpdateRemoteVolume(name, state, size, hash, suppressCleanup, new TimeSpan(0), null)
.ConfigureAwait(false);
}
+ ///
+ /// Updates the state of a remote volume in the database.
+ ///
+ /// The name of the remote volume to update.
+ /// The new state of the remote volume.
+ /// The size of the remote volume in bytes.
+ /// The hash of the remote volume, or null if not applicable.
+ /// If true, suppresses cleanup of the remote volume after updating.
+ /// The time after which the remote volume can be deleted.
+ /// If true, sets the remote volume as archived.
+ /// A task that completes when the remote volume has been updated.
public async Task UpdateRemoteVolume(string name, RemoteVolumeState state, long size, string? hash, bool suppressCleanup, TimeSpan deleteGraceTime, bool? setArchived)
{
var c = await m_updateremotevolumeCommand.SetTransaction(m_rtr)
@@ -461,6 +609,10 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Gets the ID and timestamp of all filesets in the database, ordered by timestamp in descending order.
+ ///
+ /// An asynchronous enumerable of key-value pairs, where each pair contains the fileset ID and its timestamp.
public async IAsyncEnumerable> FilesetTimes()
{
using var cmd = await m_connection.CreateCommandAsync(@"
@@ -480,6 +632,15 @@ namespace Duplicati.Library.Main.Database
);
}
+ ///
+ /// Generates a SQL WHERE clause for filtering file lists based on a specified time and versions.
+ ///
+ /// The time to filter files by. If Ticks is 0, it will not be used in the query.
+ /// An array of versions to filter files by. If null or empty, it will not be used in the query.
+ /// An optional list of filesets to use for filtering. If null, it will fetch all filesets from the database.
+ /// If true, matches files with the exact timestamp; otherwise, matches files with timestamps less than or equal to the specified time.
+ /// A task that, when awaited, returns a tuple containing the SQL WHERE clause and a dictionary of parameter values.
+ /// Thrown if the provided time is unspecified.
public async Task<(string Query, Dictionary Values)> GetFilelistWhereClause(DateTime time, long[]? versions, IEnumerable>? filesetslist = null, bool singleTimeMatch = false)
{
KeyValuePair[] filesets;
@@ -540,6 +701,11 @@ namespace Duplicati.Library.Main.Database
return (query.ToString(), args);
}
+ ///
+ /// Gets the ID of a remote volume by its name.
+ ///
+ /// The name of the remote volume.
+ /// A task that, when awaited, returns the ID of the remote volume. If the volume does not exist, it returns -1.
public async Task GetRemoteVolumeID(string file)
{
return await m_selectremotevolumeIdCommand
@@ -549,6 +715,11 @@ namespace Duplicati.Library.Main.Database
.ConfigureAwait(false);
}
+ ///
+ /// Gets the IDs of remote volumes for a list of files.
+ ///
+ /// An enumerable collection of file names.
+ /// An asynchronous enumerable of key-value pairs, where each pair contains the file name and its corresponding remote volume ID.
public async IAsyncEnumerable> GetRemoteVolumeIDs(IEnumerable files)
{
using var cmd = await m_connection.CreateCommandAsync(@"
@@ -571,6 +742,11 @@ namespace Duplicati.Library.Main.Database
yield return new KeyValuePair(rd.ConvertValueToString(0) ?? "", rd.ConvertValueToInt64(1));
}
+ ///
+ /// Gets a remote volume entry by its file name.
+ ///
+ /// The name of the remote volume file.
+ /// A task that, when awaited, returns a representing the remote volume. If the volume does not exist, it returns an empty entry.
public async Task GetRemoteVolume(string file)
{
m_selectremotevolumeCommand
@@ -593,6 +769,10 @@ namespace Duplicati.Library.Main.Database
return RemoteVolumeEntry.Empty;
}
+ ///
+ /// Gets remote volumes that are duplicates, meaning they have the same name but different states.
+ ///
+ /// An asynchronous enumerable of key-value pairs, where each pair contains the name of the remote volume and its state.
public async IAsyncEnumerable> DuplicateRemoteVolumes()
{
m_selectduplicateRemoteVolumesCommand.SetTransaction(m_rtr);
@@ -608,6 +788,10 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Gets all remote volumes from the database.
+ ///
+ /// An asynchronous enumerable of representing all remote volumes.
public async IAsyncEnumerable GetRemoteVolumes()
{
m_selectremotevolumesCommand.SetTransaction(m_rtr);
@@ -631,11 +815,12 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Log an operation performed on the remote backend
+ /// Log an operation performed on the remote backend.
///
- /// The operation performed
- /// The path involved
- /// Any data relating to the operation
+ /// The operation performed.
+ /// The path involved.
+ /// Any data relating to the operation.
+ /// A task that completes when the operation log has been recorded.
public async Task LogRemoteOperation(string operation, string path, string? data)
{
await m_insertremotelogCommand
@@ -650,11 +835,12 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Log a debug message
+ /// Log a debug message.
///
- /// The message type
- /// The message
- /// An optional exception
+ /// The message type.
+ /// The message.
+ /// An optional exception.
+ /// A task that completes when the log message has been recorded.
public async Task LogMessage(string type, string message, Exception? exception)
{
await m_insertlogCommand
@@ -668,6 +854,14 @@ namespace Duplicati.Library.Main.Database
.ConfigureAwait(false);
}
+ ///
+ /// Unlinks a remote volume from the database.
+ /// This operation removes the remote volume entry from the database without deleting the actual volume.
+ ///
+ /// The name of the remote volume to unlink.
+ /// The state of the remote volume to unlink.
+ /// A task that completes when the remote volume has been unlinked.
+ /// Thrown if the number of unlinked remote volumes is not equal to 1.
public async Task UnlinkRemoteVolume(string name, RemoteVolumeState state)
{
using var cmd = await m_connection.CreateCommandAsync(@"
@@ -688,11 +882,21 @@ namespace Duplicati.Library.Main.Database
throw new Exception($"Unexpected number of remote volumes deleted: {c}, expected {1}");
}
+ ///
+ /// Removes a remote volume from the database.
+ ///
+ /// The name of the remote volume to remove.
+ /// A task that completes when the remote volume has been removed.
public async Task RemoveRemoteVolume(string name)
{
await RemoveRemoteVolumes([name]).ConfigureAwait(false);
}
+ ///
+ /// Removes multiple remote volumes from the database.
+ ///
+ /// An enumerable collection of names of remote volumes to remove.
+ /// A task that completes when the remote volumes have been removed.
public async Task RemoveRemoteVolumes(IEnumerable names)
{
if (names == null || !names.Any()) return;
@@ -1031,6 +1235,12 @@ namespace Duplicati.Library.Main.Database
throw new ConstraintException($"Detected {nonAttachedFiles} file(s) in FilesetEntry without corresponding FileLookup entry");
}
+ ///
+ /// Performs a VACUUM operation on the database to reclaim unused space.
+ /// This operation can help optimize the database performance by defragmenting it.
+ /// Note: This operation can take a significant amount of time depending on the size of the database.
+ ///
+ /// A task that completes when the VACUUM operation has finished.
public async Task Vacuum()
{
m_hasExecutedVacuum = true;
@@ -1039,24 +1249,56 @@ namespace Duplicati.Library.Main.Database
await cmd.ExecuteNonQueryAsync("VACUUM").ConfigureAwait(false);
}
+ ///
+ /// Registers a new remote volume in the database.
+ ///
+ /// The name of the remote volume.
+ /// The type of the remote volume.
+ /// The size of the remote volume in bytes. Use -1 for unknown size.
+ /// The state of the remote volume.
+ /// A task that, when awaited, returns the ID of the newly registered remote volume.
public async Task RegisterRemoteVolume(string name, RemoteVolumeType type, long size, RemoteVolumeState state)
{
return await RegisterRemoteVolume(name, type, state, size, new TimeSpan(0))
.ConfigureAwait(false);
}
+ ///
+ /// Registers a new remote volume in the database.
+ ///
+ /// The name of the remote volume.
+ /// The type of the remote volume.
+ /// The state of the remote volume.
+ /// A task that, when awaited, returns the ID of the newly registered remote volume.
public async Task RegisterRemoteVolume(string name, RemoteVolumeType type, RemoteVolumeState state)
{
return await RegisterRemoteVolume(name, type, state, new TimeSpan(0))
.ConfigureAwait(false);
}
+ ///
+ /// Registers a new remote volume in the database.
+ ///
+ /// The name of the remote volume.
+ /// The type of the remote volume.
+ /// The state of the remote volume.
+ /// The time after which the remote volume can be deleted.
+ /// A task that, when awaited, returns the ID of the newly registered remote volume.
public async Task RegisterRemoteVolume(string name, RemoteVolumeType type, RemoteVolumeState state, TimeSpan deleteGraceTime)
{
return await RegisterRemoteVolume(name, type, state, -1, deleteGraceTime)
.ConfigureAwait(false);
}
+ ///
+ /// Registers a new remote volume in the database.
+ ///
+ /// The name of the remote volume.
+ /// The type of the remote volume.
+ /// The state of the remote volume.
+ /// The size of the remote volume in bytes. Use -1 for unknown size.
+ /// The time after which the remote volume can be deleted.
+ /// A task that, when awaited, returns the ID of the newly registered remote volume.
public async Task RegisterRemoteVolume(string name, RemoteVolumeType type, RemoteVolumeState state, long size, TimeSpan deleteGraceTime)
{
var r = await m_createremotevolumeCommand
@@ -1075,6 +1317,16 @@ namespace Duplicati.Library.Main.Database
return r;
}
+ ///
+ /// Retrieves the IDs of filesets that match a specific restore time and optional versions.
+ /// If no filesets match the criteria, it returns the newest fileset ID.
+ ///
+ /// The time to restore from.
+ /// Optional array of versions to match against the filesets.
+ /// If true, only match filesets that exactly match the restore time.
+ /// An asynchronous enumerable of fileset IDs that match the criteria.
+ /// Thrown if the provided DateTime is unspecified.
+ /// Thrown if no backups are found at the specified date.
public async IAsyncEnumerable GetFilesetIDs(DateTime restoretime, long[]? versions, bool singleTimeMatch = false)
{
if (restoretime.Kind == DateTimeKind.Unspecified)
@@ -1118,6 +1370,12 @@ namespace Duplicati.Library.Main.Database
yield return el;
}
+ ///
+ /// Finds filesets that match a specific restore time and optional versions.
+ ///
+ /// The time to restore from.
+ /// Optional array of versions to match against the filesets.
+ /// An asynchronous task that returns a collection of fileset IDs that match the criteria.
public async Task> FindMatchingFilesets(DateTime restoretime, long[]? versions)
{
if (restoretime.Kind == DateTimeKind.Unspecified)
@@ -1143,6 +1401,12 @@ namespace Duplicati.Library.Main.Database
return res;
}
+ ///
+ /// Checks if a fileset is a full backup.
+ /// A fileset is considered a full backup if its "IsFullBackup" field is set to FULL_BACKUP.
+ ///
+ /// The timestamp of the fileset to check.
+ /// A task that, when awaited, returns true if the fileset is a full backup, otherwise false.
public async Task IsFilesetFullBackup(DateTime filesetTime)
{
using var cmd = m_connection.CreateCommand();
@@ -1161,6 +1425,10 @@ namespace Duplicati.Library.Main.Database
return isFullBackup == BackupType.FULL_BACKUP;
}
+ ///
+ /// Retrieves a list of database options from the "Configuration" table.
+ ///
+ /// An asynchronous enumerable of key-value pairs representing the database options.
private async IAsyncEnumerable> GetDbOptionList()
{
using var cmd = m_connection.CreateCommand(@"
@@ -1179,6 +1447,10 @@ namespace Duplicati.Library.Main.Database
);
}
+ ///
+ /// Retrieves all database options as a dictionary.
+ ///
+ /// A task that, when awaited, returns a dictionary containing all database options.
public async Task> GetDbOptions()
{
var res = await GetDbOptionList()
@@ -1189,10 +1461,10 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Updates a database option
+ /// Updates a database option.
///
- /// The key to update
- /// The value to set
+ /// The key to update.
+ /// The value to set.
private async Task UpdateDbOption(string key, bool value)
{
var opts = await GetDbOptions().ConfigureAwait(false);
@@ -1207,8 +1479,10 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Flag indicating if a repair is in progress
+ /// Flag indicating if a repair is in progress.
///
+ /// Optional value to set the flag to. If null, the current value in the database is returned.
+ /// A task that, when awaited, returns true if a repair is in progress, otherwise false.
public async Task RepairInProgress(bool? value = null)
{
if (value is bool v)
@@ -1224,8 +1498,10 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Flag indicating if a repair is in progress
+ /// Flag indicating if the database has been partially recreated.
///
+ /// Optional value to set the flag to. If null, the current value in the database is returned.
+ /// A task that, when awaited, returns true if the database has been partially recreated, otherwise false.
public async Task PartiallyRecreated(bool? value = null)
{
if (value is bool v)
@@ -1241,8 +1517,10 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Flag indicating if the database can contain partial uploads
+ /// Flag indicating if the database has been terminated with active uploads.
///
+ /// Optional value to set the flag to. If null, the current value in the database is returned.
+ /// A task that, when awaited, returns true if the database has been terminated with active uploads, otherwise false.
public async Task TerminatedWithActiveUploads(bool? value = null)
{
if (value is bool v)
@@ -1258,10 +1536,10 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Sets the database options
+ /// Sets the database options.
///
- /// The options to set
- /// An optional transaction
+ /// The options to set.
+ /// A task that completes when the options have been set.
public async Task SetDbOptions(IDictionary options)
{
using var cmd = m_connection.CreateCommand();
@@ -1289,6 +1567,11 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Counts the number of blocks larger than a specified size.
+ ///
+ /// The size in bytes to compare against.
+ /// A task that, when awaited, returns the count of blocks larger than the specified size.
public async Task GetBlocksLargerThan(long fhblocksize)
{
using var cmd = await m_connection.CreateCommandAsync(@"
@@ -1305,35 +1588,35 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Verifies the consistency of the database
+ /// Verifies the consistency of the database.
///
- /// The block size in bytes
- /// The hash size in byts
- /// Also verify filelists (can be slow)
- /// The transaction to run in
+ /// The block size in bytes.
+ /// The hash size in bytes.
+ /// Also verify filelists (can be slow).
+ /// A task that completes when the consistency check is finished.
public async Task VerifyConsistency(long blocksize, long hashsize, bool verifyfilelists)
=> await VerifyConsistencyInner(blocksize, hashsize, verifyfilelists, false)
.ConfigureAwait(false);
///
- /// Verifies the consistency of the database prior to repair
+ /// Verifies the consistency of the database prior to repair.
///
- /// The block size in bytes
- /// The hash size in byts
- /// Also verify filelists (can be slow)
- /// The transaction to run in
+ /// The block size in bytes.
+ /// The hash size in bytes.
+ /// Also verify filelists (can be slow).
+ /// A task that completes when the consistency check is finished.
public async Task VerifyConsistencyForRepair(long blocksize, long hashsize, bool verifyfilelists)
=> await VerifyConsistencyInner(blocksize, hashsize, verifyfilelists, true)
.ConfigureAwait(false);
///
- /// Verifies the consistency of the database
+ /// Verifies the consistency of the database.
///
- /// The block size in bytes
- /// The hash size in byts
- /// Also verify filelists (can be slow)
- /// Disable verify for errors that will be fixed by repair
- /// The transaction to run in
+ /// The block size in bytes.
+ /// The hash size in bytes.
+ /// Also verify filelists (can be slow).
+ /// Disable verify for errors that will be fixed by repair.
+ /// A task that completes when the consistency check is finished.
private async Task VerifyConsistencyInner(long blocksize, long hashsize, bool verifyfilelists, bool laxVerifyForRepair)
{
using var cmd = m_connection.CreateCommand()
@@ -1692,18 +1975,37 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Represents a block in the database.
+ ///
public interface IBlock
{
+ ///
+ /// Gets the hash of the block.
+ ///
string Hash { get; }
+ ///
+ /// Gets the size of the block in bytes.
+ ///
long Size { get; }
}
+ ///
+ /// Represents a block in the database with its hash and size.
+ ///
+ /// The hash of the block.
+ /// The size of the block in bytes.
internal class Block(string hash, long size) : IBlock
{
public string Hash { get; private set; } = hash;
public long Size { get; private set; } = size;
}
+ ///
+ /// Retrieves all blocks associated with a specific volume ID.
+ ///
+ /// The ID of the volume to retrieve blocks for.
+ /// An asynchronous enumerable of blocks associated with the specified volume ID.
public async IAsyncEnumerable GetBlocks(long volumeid)
{
using var cmd = await m_connection.CreateCommandAsync(@"
@@ -1726,25 +2028,55 @@ namespace Duplicati.Library.Main.Database
);
}
+ ///
+ /// An asynchronous enumerable that retrieves blocklist hashes for files in a specific fileset.
+ ///
private class BlocklistHashEnumerable : IAsyncEnumerable
{
+ ///
+ /// An asynchronous enumerator for the blocklist hashes.
+ ///
private class BlocklistHashEnumerator : IDisposable, IAsyncEnumerator
{
+ ///
+ /// The data reader used to read blocklist hashes.
+ ///
private readonly SqliteDataReader m_reader;
+ ///
+ /// The parent enumerable that this enumerator belongs to.
+ ///
private readonly BlocklistHashEnumerable m_parent;
+ ///
+ /// The path of the current file being processed.
+ ///
private string? m_path = null;
+ ///
+ /// Indicates if this is the first entry being processed.
+ ///
private bool m_first = true;
+ ///
+ /// The current blocklist hash being processed.
+ ///
private string? m_current = null;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The parent enumerable that this enumerator belongs to.
+ /// The data reader used to read blocklist hashes.
public BlocklistHashEnumerator(BlocklistHashEnumerable parent, SqliteDataReader reader)
{
m_reader = reader;
m_parent = parent;
}
+ ///
+ /// Gets the current blocklist hash.
+ ///
public string Current { get { return m_current!; } }
public void Dispose() { }
+ // The warning is suppressed because the interface requires the method, but there's nothing to dispose.
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public async ValueTask DisposeAsync() { }
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
@@ -1783,6 +2115,9 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Resets the enumerator to its initial state.
+ ///
public void Reset()
{
if (!m_first)
@@ -1792,16 +2127,28 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// The data reader used to read blocklist hashes.
+ ///
private readonly SqliteDataReader m_reader;
+ ///
+ /// Initializes a new instance of the class.
+ ///
public BlocklistHashEnumerable(SqliteDataReader reader)
{
m_reader = reader;
MoreData = true;
}
+ ///
+ /// Indicates if there is more data to read.
+ ///
public bool MoreData { get; protected set; }
+ ///
+ /// Gets an enumerator that iterates through the blocklist hashes.
+ ///
public IAsyncEnumerator GetEnumerator()
{
return new BlocklistHashEnumerator(this, m_reader);
@@ -1813,6 +2160,9 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// SQL query for listing all filesets in the database, including their metadata and blocklist hashes.
+ ///
public const string LIST_FILESETS = @"
SELECT
""L"".""Path"",
@@ -1894,6 +2244,9 @@ namespace Duplicati.Library.Main.Database
ON ""M"".""BlocksetID"" = ""L"".""MetablocksetID""
";
+ ///
+ /// SQL query for listing folders and symlinks in a specific fileset, including their metadata and blocklist hashes.
+ ///
public const string LIST_FOLDERS_AND_SYMLINKS = @"
SELECT
""G"".""BlocksetID"",
@@ -1941,6 +2294,12 @@ namespace Duplicati.Library.Main.Database
""H"".""Index""
";
+ ///
+ /// Writes the contents of a fileset to a specified volume writer.
+ ///
+ /// The volume writer to which the fileset will be written.
+ /// The ID of the fileset to write.
+ /// A task that completes when the fileset has been written.
public async Task WriteFileset(Volumes.FilesetVolumeWriter filesetvolume, long filesetId)
{
using var cmd = m_connection.CreateCommand()
@@ -2027,6 +2386,12 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Links a fileset to a specific volume by updating the VolumeID in the Fileset table.
+ ///
+ /// The ID of the fileset to link.
+ /// The ID of the volume to link the fileset to.
+ /// A task that completes when the link operation is finished.
public async Task LinkFilesetToVolume(long filesetid, long volumeid)
{
using var cmd = await m_connection.CreateCommandAsync(@"
@@ -2046,6 +2411,11 @@ namespace Duplicati.Library.Main.Database
throw new Exception($"Failed to link filesetid {filesetid} to volumeid {volumeid}");
}
+ ///
+ /// Pushes timestamp changes from the latest version of a fileset to the previous version.
+ ///
+ /// The ID of the fileset whose timestamp changes will be pushed.
+ /// A task that completes when the timestamp changes have been pushed.
public async Task PushTimestampChangesToPreviousVersion(long filesetId)
{
var query = @"
@@ -2073,13 +2443,23 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Keeps a list of filenames in a temporary table with a single column Path
+ /// Keeps a list of filenames in a temporary table with a single column Path.
///
public class FilteredFilenameTable : IDisposable, IAsyncDisposable
{
+ ///
+ /// The name of the temporary table that holds the filtered filenames.
+ ///
public string Tablename { get; private set; }
+ ///
+ /// The database used to create and manage the temporary table.
+ ///
private readonly LocalDatabase m_db;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The database to use for creating the temporary table.
private FilteredFilenameTable(LocalDatabase db)
{
Tablename = "Filenames-" + Library.Utility.Utility.ByteArrayAsHexString(Guid.NewGuid().ToByteArray());
@@ -2092,6 +2472,12 @@ namespace Duplicati.Library.Main.Database
throw new NotImplementedException("Use the Create method instead.");
}
+ ///
+ /// Creates a new instance of the class asynchronously.
+ ///
+ /// The database to use for creating the temporary table.
+ /// The filter to apply to the filenames.
+ /// A task that represents the asynchronous operation, with a as the result.
public static async Task CreateFilteredFilenameTableAsync(LocalDatabase db, IFilter filter)
{
var ftt = new FilteredFilenameTable(db);
@@ -2228,6 +2614,13 @@ namespace Duplicati.Library.Main.Database
}
}
+ ///
+ /// Renames a remote file in the database, preserving its ID links.
+ ///
+ /// The current name of the remote file.
+ /// The new name for the remote file.
+ /// A task that completes when the renaming operation is finished.
+ /// Thrown if the renaming operation does not affect exactly one row.
public async Task RenameRemoteFile(string oldname, string newname)
{
//Rename the old entry, to preserve ID links
@@ -2280,9 +2673,9 @@ namespace Duplicati.Library.Main.Database
///
/// Creates a timestamped backup operation to correctly associate the fileset with the time it was created.
///
- /// The ID of the fileset volume to update
- /// The timestamp of the operation to create
- /// An optional external transaction
+ /// The ID of the fileset volume to update.
+ /// The timestamp of the operation to create.
+ /// A task that when awaited contains the ID of the newly created fileset.
public virtual async Task CreateFileset(long volumeid, DateTime timestamp)
{
using var cmd = await m_connection.CreateCommandAsync(@"
@@ -2319,7 +2712,8 @@ namespace Duplicati.Library.Main.Database
///
/// The ID of the index volume.
/// The ID of the block volume.
- /// An optional transaction.
+ /// A task that completes when the link has been added.
+ /// Thrown if either volume ID is less than or equal to 0.
public async Task AddIndexBlockLink(long indexVolumeID, long blockVolumeID)
{
if (indexVolumeID <= 0)
@@ -2336,12 +2730,11 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Returns all unique blocklists for a given volume
+ /// Returns all unique blocklists for a given volume.
///
- /// The volume ID to get blocklists for
- /// The blocksize
- /// The size of the hash
- /// An optional external transaction
+ /// The volume ID to get blocklists for.
+ /// The blocksize.
+ /// The size of the hash.
/// An enumerable of tuples containing the blocklist hash, the blocklist data and the length of the data
public async IAsyncEnumerable<(string Hash, byte[] Buffer, int Size)> GetBlocklists(long volumeid, long blocksize, int hashsize)
{
@@ -2435,11 +2828,11 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Update fileset with full backup state
+ /// Update fileset with full backup state.
///
- /// Existing file set to update
- /// Full backup state
- /// An optional external transaction
+ /// Existing file set to update.
+ /// Full backup state.
+ /// A task that completes when the update is finished.
public async Task UpdateFullBackupStateInFileset(long fileSetId, bool isFullBackup)
{
using var cmd = await m_connection.CreateCommandAsync(@"
@@ -2457,10 +2850,10 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Removes all entries in the fileset entry table for a given fileset ID
+ /// Removes all entries in the fileset entry table for a given fileset ID.
///
- /// The fileset ID to clear
- /// The transaction to use
+ /// The fileset ID to clear.
+ /// A task that completes when the entries have been cleared.
public async Task ClearFilesetEntries(long filesetId)
{
using var cmd = await m_connection.CreateCommandAsync(@"
@@ -2476,10 +2869,10 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Gets the last previous fileset that was incomplete
+ /// Gets the last previous fileset that was incomplete.
///
- /// The transaction to use
- /// The last incomplete fileset or default
+ /// The transaction to use.
+ /// A task that when awaited returns the last incomplete fileset or default
public async Task GetLastIncompleteFilesetVolume()
{
var candidates = GetIncompleteFilesets()
@@ -2497,10 +2890,9 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Gets a list of incomplete filesets
+ /// Gets a list of incomplete filesets.
///
- /// An optional transaction
- /// A list of fileset IDs and timestamps
+ /// An asynchronous enumerable of key-value pairs where the key is the fileset ID and the value is the timestamp of the fileset.
public async IAsyncEnumerable> GetIncompleteFilesets()
{
using var cmd = await m_connection.CreateCommandAsync(@$"
@@ -2538,11 +2930,10 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Gets the remote volume entry from the fileset ID
+ /// Gets the remote volume entry from the fileset ID.
///
- /// The fileset ID
- /// An optional transaction
- /// The remote volume entry or default
+ /// The fileset ID.
+ /// A task that when awaited returns the remote volume entry associated with the fileset ID, or default if not found.
public async Task GetRemoteVolumeFromFilesetID(long filesetID)
{
using var cmd = await m_connection.CreateCommandAsync(@"
@@ -2584,6 +2975,11 @@ namespace Duplicati.Library.Main.Database
return default(RemoteVolumeEntry);
}
+ ///
+ /// Purges log data and remote operations older than the specified threshold.
+ ///
+ /// The threshold date and time; all log data and remote operations older than this will be purged.
+ /// A task that completes when the purge operation is finished.
public async Task PurgeLogData(DateTime threshold)
{
using var cmd = m_connection.CreateCommand(m_rtr);
@@ -2608,6 +3004,11 @@ namespace Duplicati.Library.Main.Database
await m_rtr.CommitAsync().ConfigureAwait(false);
}
+ ///
+ /// Purges deleted remote volumes that have not been modified since the specified threshold.
+ ///
+ /// The threshold date and time; all deleted remote volumes older than this will be purged.
+ /// A task that completes when the purge operation is finished.
public async Task PurgeDeletedVolumes(DateTime threshold)
{
await m_removedeletedremotevolumeCommand
@@ -2665,11 +3066,11 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Disposes all fields of a certain type, in the instance and its bases
+ /// Disposes all fields of a certain type, in the instance and its bases.
///
- /// The type of fields to find
- /// The item to dispose
- /// True if an aggregate exception should be thrown, or false if exceptions are silently captured
+ /// The type of fields to find.
+ /// The item to dispose.
+ /// True if an aggregate exception should be thrown, or false if exceptions are silently captured.
public static void DisposeAllFields(object item, bool throwExceptions)
where T : IDisposable
{
@@ -2711,6 +3112,11 @@ namespace Duplicati.Library.Main.Database
throw new AggregateException(exceptions);
}
+ ///
+ /// Writes the results of a basic operation to the log.
+ ///
+ /// The results to write.
+ /// A task that completes when the results have been written.
public async Task WriteResults(IBasicResults result)
{
if (IsDisposed)
@@ -2735,20 +3141,19 @@ namespace Duplicati.Library.Main.Database
}
///
- /// The current index into the path prefix buffer
+ /// The current index into the path prefix buffer.
///
private int m_pathPrefixIndex = 0;
///
- /// The path prefix lookup list
+ /// The path prefix lookup list.
///
private readonly KeyValuePair[] m_pathPrefixLookup = new KeyValuePair[5];
///
- /// Gets the path prefix ID, optionally creating it in the process.
+ /// Gets the path prefix ID, optionally creating it in the process..
///
- /// The path prefix ID.
/// The path to get the prefix for.
- /// The transaction to use for insertion, or null for no transaction
+ /// A task that when awaited returns the path prefix ID.
public async Task GetOrCreatePathPrefix(string prefix)
{
// Ring-buffer style lookup
@@ -2781,7 +3186,7 @@ namespace Duplicati.Library.Main.Database
}
///
- /// The path separators on this system
+ /// The path separators on this system.
///
private static readonly char[] _pathseparators = [
Path.DirectorySeparatorChar,
@@ -2789,10 +3194,10 @@ namespace Duplicati.Library.Main.Database
];
///
- /// Helper method that splits a path on the last path separator
+ /// Helper method that splits a path on the last path separator.
///
- /// The prefix and name.
/// The path to split.
+ /// The prefix and name.
public static KeyValuePair SplitIntoPrefixAndName(string path)
{
if (string.IsNullOrEmpty(path))
@@ -2807,11 +3212,18 @@ namespace Duplicati.Library.Main.Database
}
///
- /// Defines the backups types
+ /// Defines the backups types.
///
public static class BackupType
{
+ ///
+ /// Indicates a partial backup.
+ ///
public const int PARTIAL_BACKUP = 0;
+ ///
+ /// Indicates a full backup.
+ ///
public const int FULL_BACKUP = 1;
}
+
}