diff --git a/Duplicati/Library/Main/Database/ExtensionMethods.cs b/Duplicati/Library/Main/Database/ExtensionMethods.cs index 87709473a..aa6364103 100644 --- a/Duplicati/Library/Main/Database/ExtensionMethods.cs +++ b/Duplicati/Library/Main/Database/ExtensionMethods.cs @@ -211,7 +211,7 @@ public static class ExtensionMethods /// The command instance to execute on /// The transaction to use for the command /// The number of rows affected - public static int ExecuteNonQuery(this IDbCommand self, IDbTransaction transaction) + public static int ExecuteNonQuery(this IDbCommand self, IDbTransaction? transaction) { self.Transaction = transaction; return self.ExecuteNonQuery(); @@ -274,7 +274,7 @@ public static class ExtensionMethods /// The transaction to use for the command /// The default value to return if no value is found /// The scalar int64 value of the first row as a string - public static long ExecuteScalarInt64(this IDbCommand self, IDbTransaction transaction, long defaultvalue = -1) + public static long ExecuteScalarInt64(this IDbCommand self, IDbTransaction? transaction, long defaultvalue = -1) { self.Transaction = transaction; return ExecuteScalarInt64(self, true, null, defaultvalue); @@ -320,7 +320,7 @@ public static class ExtensionMethods /// The command string to execute /// The values to use as parameters. The parameters must already be added. /// A instance - public static IDataReader ExecuteReader(this IDbCommand self, string cmd, Dictionary values) + public static IDataReader ExecuteReader(this IDbCommand self, string cmd, Dictionary? values) { return ExecuteReader(self, true, cmd, values); } @@ -361,7 +361,7 @@ public static class ExtensionMethods /// The command string to execute /// The values to use as parameters. The parameters must already be added. /// A instance - public static IDataReader ExecuteReader(this IDbCommand self, bool writeLog, string? cmd, Dictionary values) + public static IDataReader ExecuteReader(this IDbCommand self, bool writeLog, string? cmd, Dictionary? values) { if (cmd != null) self.SetCommandAndParameters(cmd); diff --git a/Duplicati/Library/Main/Database/LocalDatabase.cs b/Duplicati/Library/Main/Database/LocalDatabase.cs index dced583d3..ebc6bd920 100644 --- a/Duplicati/Library/Main/Database/LocalDatabase.cs +++ b/Duplicati/Library/Main/Database/LocalDatabase.cs @@ -18,6 +18,9 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. + +#nullable enable + using System; using System.Collections.Generic; using System.Data; @@ -83,7 +86,7 @@ namespace Duplicati.Library.Main.Database { path = Path.GetFullPath(path); if (!Directory.Exists(Path.GetDirectoryName(path))) - Directory.CreateDirectory(Path.GetDirectoryName(path)); + Directory.CreateDirectory(Path.GetDirectoryName(path) ?? throw new DirectoryNotFoundException("Path was a root folder.")); var c = SQLiteHelper.SQLiteLoader.LoadConnection(path); @@ -174,6 +177,7 @@ namespace Duplicati.Library.Main.Database private LocalDatabase(IDbConnection connection) { + m_connection = connection; m_insertlogCommand = connection.CreateCommand(@"INSERT INTO ""LogData"" (""OperationID"", ""Timestamp"", ""Type"", ""Message"", ""Exception"") VALUES (@OperationID, @Timestamp, @Type, @Message, @Exception)"); m_insertremotelogCommand = connection.CreateCommand(@"INSERT INTO ""RemoteOperation"" (""OperationID"", ""Timestamp"", ""Operation"", ""Path"", ""Data"") VALUES (@OperationID, @Timestamp, @Operation, @Path, @Data)"); m_updateremotevolumeCommand = connection.CreateCommand(@"UPDATE ""Remotevolume"" SET ""OperationID"" = @OperationID, ""State"" = @State, ""Hash"" = @Hash, ""Size"" = @Size WHERE ""Name"" = @Name"); @@ -197,17 +201,17 @@ namespace Duplicati.Library.Main.Database return Library.Utility.Utility.EPOCH.AddSeconds(seconds); } - public void UpdateRemoteVolume(string name, RemoteVolumeState state, long size, string hash, IDbTransaction transaction = null) + public void UpdateRemoteVolume(string name, RemoteVolumeState state, long size, string hash, IDbTransaction? transaction = null) { UpdateRemoteVolume(name, state, size, hash, false, transaction); } - public void UpdateRemoteVolume(string name, RemoteVolumeState state, long size, string hash, bool suppressCleanup, IDbTransaction transaction = null) + public void UpdateRemoteVolume(string name, RemoteVolumeState state, long size, string hash, bool suppressCleanup, IDbTransaction? transaction = null) { UpdateRemoteVolume(name, state, size, hash, suppressCleanup, new TimeSpan(0), transaction); } - public void UpdateRemoteVolume(string name, RemoteVolumeState state, long size, string hash, bool suppressCleanup, TimeSpan deleteGraceTime, IDbTransaction transaction = null) + public void UpdateRemoteVolume(string name, RemoteVolumeState state, long size, string hash, bool suppressCleanup, TimeSpan deleteGraceTime, IDbTransaction? transaction = null) { m_updateremotevolumeCommand.Transaction = transaction; var c = m_updateremotevolumeCommand.SetParameterValue("@OperationID", m_operationid) @@ -253,11 +257,11 @@ namespace Duplicati.Library.Main.Database } } - public (string Query, Dictionary Values) GetFilelistWhereClause(DateTime time, long[] versions, IEnumerable> filesetslist = null, bool singleTimeMatch = false) + public (string Query, Dictionary Values) GetFilelistWhereClause(DateTime time, long[] versions, IEnumerable>? filesetslist = null, bool singleTimeMatch = false) { var filesets = (filesetslist ?? FilesetTimes).ToArray(); var query = new StringBuilder(); - var args = new Dictionary(); + var args = new Dictionary(); if (time.Ticks > 0 || (versions != null && versions.Length > 0)) { var hasTime = false; @@ -306,13 +310,13 @@ namespace Duplicati.Library.Main.Database return (query.ToString(), args); } - public long GetRemoteVolumeID(string file, IDbTransaction transaction = null) + public long GetRemoteVolumeID(string file, IDbTransaction? transaction = null) { m_selectremotevolumeIdCommand.Transaction = transaction; return m_selectremotevolumeIdCommand.SetParameterValue("@Name", file).ExecuteScalarInt64(-1); } - public IEnumerable> GetRemoteVolumeIDs(IEnumerable files, IDbTransaction transaction = null) + public IEnumerable> GetRemoteVolumeIDs(IEnumerable files, IDbTransaction? transaction = null) { using (var cmd = m_connection.CreateCommand(transaction)) { @@ -326,7 +330,7 @@ namespace Duplicati.Library.Main.Database } } - public RemoteVolumeEntry GetRemoteVolume(string file, IDbTransaction transaction = null) + public RemoteVolumeEntry GetRemoteVolume(string file, IDbTransaction? transaction = null) { m_selectremotevolumeCommand.Transaction = transaction; m_selectremotevolumeCommand.SetParameterValue("@Name", file); @@ -334,11 +338,11 @@ namespace Duplicati.Library.Main.Database if (rd.Read()) return new RemoteVolumeEntry( rd.ConvertValueToInt64(0), - rd.GetValue(1).ToString(), - (rd.GetValue(4) == null || rd.GetValue(4) == DBNull.Value) ? null : rd.GetValue(4).ToString(), + rd.ConvertValueToString(1), + rd.ConvertValueToString(4), rd.ConvertValueToInt64(3, -1), - (RemoteVolumeType)Enum.Parse(typeof(RemoteVolumeType), rd.GetValue(2).ToString()), - (RemoteVolumeState)Enum.Parse(typeof(RemoteVolumeState), rd.GetValue(5).ToString()), + (RemoteVolumeType)Enum.Parse(typeof(RemoteVolumeType), rd.ConvertValueToString(2) ?? ""), + (RemoteVolumeState)Enum.Parse(typeof(RemoteVolumeState), rd.ConvertValueToString(5) ?? ""), ParseFromEpochSeconds(rd.ConvertValueToInt64(6, 0)) ); @@ -347,16 +351,16 @@ namespace Duplicati.Library.Main.Database public IEnumerable> DuplicateRemoteVolumes() { - foreach (var rd in m_selectduplicateRemoteVolumesCommand.ExecuteReaderEnumerable(null)) + foreach (var rd in m_selectduplicateRemoteVolumesCommand.ExecuteReaderEnumerable()) { yield return new KeyValuePair( - rd.GetValue(0).ToString(), - (RemoteVolumeState)Enum.Parse(typeof(RemoteVolumeState), rd.GetValue(1).ToString()) + rd.ConvertValueToString(0) ?? throw new Exception("Name was null"), + (RemoteVolumeState)Enum.Parse(typeof(RemoteVolumeState), rd.ConvertValueToString(1) ?? "") ); } } - public IEnumerable GetRemoteVolumes(IDbTransaction transaction = null) + public IEnumerable GetRemoteVolumes(IDbTransaction? transaction = null) { m_selectremotevolumesCommand.Transaction = transaction; using (var rd = m_selectremotevolumesCommand.ExecuteReader()) @@ -365,11 +369,11 @@ namespace Duplicati.Library.Main.Database { yield return new RemoteVolumeEntry( rd.ConvertValueToInt64(0), - rd.GetValue(1).ToString(), - (rd.GetValue(4) == null || rd.GetValue(4) == DBNull.Value) ? null : rd.GetValue(4).ToString(), + rd.ConvertValueToString(1), + rd.ConvertValueToString(4), rd.ConvertValueToInt64(3, -1), - (RemoteVolumeType)Enum.Parse(typeof(RemoteVolumeType), rd.GetValue(2).ToString()), - (RemoteVolumeState)Enum.Parse(typeof(RemoteVolumeState), rd.GetValue(5).ToString()), + (RemoteVolumeType)Enum.Parse(typeof(RemoteVolumeType), rd.ConvertValueToString(2) ?? ""), + (RemoteVolumeState)Enum.Parse(typeof(RemoteVolumeState), rd.ConvertValueToString(5) ?? ""), ParseFromEpochSeconds(rd.ConvertValueToInt64(6, 0)) ); } @@ -399,7 +403,7 @@ namespace Duplicati.Library.Main.Database /// The message type /// The message /// An optional exception - public void LogMessage(string type, string message, Exception exception, IDbTransaction transaction) + public void LogMessage(string type, string message, Exception? exception, IDbTransaction? transaction) { m_insertlogCommand.SetParameterValue("@OperationID", m_operationid) .SetParameterValue("@Timestamp", Library.Utility.Utility.NormalizeDateTimeToEpochSeconds(DateTime.UtcNow)) @@ -409,7 +413,7 @@ namespace Duplicati.Library.Main.Database .ExecuteNonQuery(transaction); } - public void UnlinkRemoteVolume(string name, RemoteVolumeState state, IDbTransaction transaction = null) + public void UnlinkRemoteVolume(string name, RemoteVolumeState state, IDbTransaction? transaction = null) { using (var tr = new TemporaryTransactionWrapper(m_connection, transaction)) using (var cmd = m_connection.CreateCommand(tr.Parent)) @@ -426,12 +430,12 @@ namespace Duplicati.Library.Main.Database } } - public void RemoveRemoteVolume(string name, IDbTransaction transaction = null) + public void RemoveRemoteVolume(string name, IDbTransaction? transaction = null) { RemoveRemoteVolumes([name], transaction); } - public void RemoveRemoteVolumes(IEnumerable names, IDbTransaction transaction = null) + public void RemoveRemoteVolumes(IEnumerable names, IDbTransaction? transaction = null) { if (names == null || !names.Any()) return; @@ -559,17 +563,17 @@ AND Fileset.ID NOT IN return RegisterRemoteVolume(name, type, state, size, new TimeSpan(0), null); } - public long RegisterRemoteVolume(string name, RemoteVolumeType type, RemoteVolumeState state, IDbTransaction transaction) + public long RegisterRemoteVolume(string name, RemoteVolumeType type, RemoteVolumeState state, IDbTransaction? transaction) { return RegisterRemoteVolume(name, type, state, new TimeSpan(0), transaction); } - public long RegisterRemoteVolume(string name, RemoteVolumeType type, RemoteVolumeState state, TimeSpan deleteGraceTime, IDbTransaction transaction) + public long RegisterRemoteVolume(string name, RemoteVolumeType type, RemoteVolumeState state, TimeSpan deleteGraceTime, IDbTransaction? transaction) { return RegisterRemoteVolume(name, type, state, -1, deleteGraceTime, transaction); } - public long RegisterRemoteVolume(string name, RemoteVolumeType type, RemoteVolumeState state, long size, TimeSpan deleteGraceTime, IDbTransaction transaction) + public long RegisterRemoteVolume(string name, RemoteVolumeType type, RemoteVolumeState state, long size, TimeSpan deleteGraceTime, IDbTransaction? transaction) { using (var tr = new TemporaryTransactionWrapper(m_connection, transaction)) { @@ -592,14 +596,11 @@ AND Fileset.ID NOT IN if (restoretime.Kind == DateTimeKind.Unspecified) throw new Exception("Invalid DateTime given, must be either local or UTC"); - var tmp = GetFilelistWhereClause(restoretime, versions); - string query = tmp.Item1; - var args = tmp.Item2; - + (var query, var values) = GetFilelistWhereClause(restoretime, versions); var res = new List(); using (var cmd = m_connection.CreateCommand()) { - using (var rd = cmd.ExecuteReader($@"SELECT ""ID"" FROM ""Fileset"" {query} ORDER BY ""Timestamp"" DESC", args)) + using (var rd = cmd.ExecuteReader($@"SELECT ""ID"" FROM ""Fileset"" {query} ORDER BY ""Timestamp"" DESC", values)) while (rd.Read()) res.Add(rd.GetInt64(0)); @@ -661,7 +662,7 @@ AND Fileset.ID NOT IN private readonly IDbTransaction m_parent; private readonly bool m_isTemporary; - public TemporaryTransactionWrapper(IDbConnection connection, IDbTransaction transaction) + public TemporaryTransactionWrapper(IDbConnection connection, IDbTransaction? transaction) { if (transaction != null) { @@ -690,15 +691,15 @@ AND Fileset.ID NOT IN public IDbTransaction Parent { get { return m_parent; } } } - private IEnumerable> GetDbOptionList(IDbTransaction transaction = null) + private IEnumerable> GetDbOptionList(IDbTransaction? transaction = null) { using (var cmd = m_connection.CreateCommand(transaction)) using (var rd = cmd.ExecuteReader(@"SELECT ""Key"", ""Value"" FROM ""Configuration"" ")) while (rd.Read()) - yield return new KeyValuePair(rd.GetValue(0).ToString(), rd.GetValue(1).ToString()); + yield return new KeyValuePair(rd.ConvertValueToString(0) ?? "", rd.ConvertValueToString(1) ?? ""); } - public IDictionary GetDbOptions(IDbTransaction transaction = null) + public IDictionary GetDbOptions(IDbTransaction? transaction = null) { return GetDbOptionList(transaction).ToDictionary(x => x.Key, x => x.Value); } @@ -752,7 +753,7 @@ AND Fileset.ID NOT IN /// /// The options to set /// An optional transaction - public void SetDbOptions(IDictionary options, IDbTransaction transaction = null) + public void SetDbOptions(IDictionary options, IDbTransaction? transaction = null) { using (var tr = new TemporaryTransactionWrapper(m_connection, transaction)) using (var cmd = m_connection.CreateCommand(tr.Parent)) @@ -1001,25 +1002,26 @@ ON } } - public IEnumerable GetBlocks(long volumeid, IDbTransaction transaction = null) + public IEnumerable GetBlocks(long volumeid, IDbTransaction? transaction = null) { - using (var cmd = m_connection.CreateCommand(transaction)) - using (var rd = cmd.SetCommandAndParameters(@"SELECT DISTINCT ""Hash"", ""Size"" FROM ""Block"" WHERE ""VolumeID"" = @VolumeId") - .SetParameterValue("@VolumeId", volumeid) - .ExecuteReader()) + using var cmd = m_connection.CreateCommand(transaction) + .SetCommandAndParameters(@"SELECT DISTINCT ""Hash"", ""Size"" FROM ""Block"" WHERE ""VolumeID"" = @VolumeId") + .SetParameterValue("@VolumeId", volumeid); + using (var rd = cmd.ExecuteReader()) while (rd.Read()) - yield return new Block(rd.GetValue(0).ToString(), rd.GetInt64(1)); + yield return new Block(rd.ConvertValueToString(0) ?? throw new Exception("Hash is null"), rd.ConvertValueToInt64(1)); } + // TODO: Replace this with an enumerable method private class BlocklistHashEnumerable : IEnumerable { private class BlocklistHashEnumerator : IEnumerator { private readonly IDataReader m_reader; private readonly BlocklistHashEnumerable m_parent; - private string m_path = null; + private string? m_path = null; private bool m_first = true; - private string m_current = null; + private string? m_current = null; public BlocklistHashEnumerator(BlocklistHashEnumerable parent, IDataReader reader) { @@ -1027,7 +1029,7 @@ ON m_parent = parent; } - public string Current { get { return m_current; } } + public string Current { get { return m_current!; } } public void Dispose() { @@ -1041,8 +1043,8 @@ ON if (m_path == null) { - m_path = m_reader.GetValue(0).ToString(); - m_current = m_reader.GetValue(6).ToString(); + m_path = m_reader.ConvertValueToString(0); + m_current = m_reader.ConvertValueToString(6); return true; } else @@ -1057,14 +1059,14 @@ ON return false; } - var np = m_reader.GetValue(0).ToString(); + var np = m_reader.ConvertValueToString(0); if (m_path != np) { m_current = null; return false; } - m_current = m_reader.GetValue(6).ToString(); + m_current = m_reader.ConvertValueToString(6); return true; } } @@ -1236,7 +1238,7 @@ ORDER BY .SetParameterValue("@FolderBlocksetId", FOLDER_BLOCKSET_ID) .SetParameterValue("@SymlinkBlocksetId", SYMLINK_BLOCKSET_ID); - string lastpath = null; + string? lastpath = null; using (var rd = cmd.ExecuteReader()) while (rd.Read()) { @@ -1269,19 +1271,19 @@ ORDER BY var more = false; do { - var path = rd.GetValue(0).ToString(); - var filehash = rd.GetValue(3).ToString(); + var path = rd.ConvertValueToString(0); + var filehash = rd.ConvertValueToString(3); var size = rd.ConvertValueToInt64(2); var lastmodified = new DateTime(rd.ConvertValueToInt64(1, 0), DateTimeKind.Utc); - var metahash = rd.GetValue(4).ToString(); + var metahash = rd.ConvertValueToString(4); var metasize = rd.ConvertValueToInt64(5, -1); var p = rd.GetValue(6); var blrd = (p == null || p == DBNull.Value) ? null : new BlocklistHashEnumerable(rd); - var blockhash = rd.GetValue(7).ToString(); + var blockhash = rd.ConvertValueToString(7); var blocksize = rd.ConvertValueToInt64(8, -1); - var metablockhash = rd.GetValue(9).ToString(); + var metablockhash = rd.ConvertValueToString(9); //var metablocksize = rd.ConvertValueToInt64(10, -1); - var metablocklisthash = rd.GetValue(11).ToString(); + var metablocklisthash = rd.ConvertValueToString(11); if (blockhash == filehash) blockhash = null; @@ -1346,7 +1348,7 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER type = expression.Type; // Bugfix: SQLite does not handle case-insensitive LIKE with non-ascii characters - if (type != FilterType.Regexp && !Library.Utility.Utility.IsFSCaseSensitive && filter.ToString().Any(x => x > 127)) + if (type != FilterType.Regexp && !Library.Utility.Utility.IsFSCaseSensitive && filter.ToString()!.Any(x => x > 127)) type = FilterType.Regexp; if (filter.Empty) @@ -1385,7 +1387,7 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER else { var sb = new StringBuilder(); - var args = new Dictionary(); + var args = new Dictionary(); foreach (var f in ((FilterExpression)filter).GetSimpleList()) { if (sb.Length != 0) @@ -1423,7 +1425,7 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER cmd.ExecuteNonQuery(FormatInvariant(@$"DROP TABLE IF EXISTS ""{Tablename}"" ")); } catch { } - finally { Tablename = null; } + finally { Tablename = null!; } } } @@ -1447,7 +1449,7 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER cmd.SetCommandAndParameters(@"SELECT ""Type"" FROM ""Remotevolume"" WHERE ""Name"" = @Name") .SetParameterValue("@Name", newname) .ExecuteScalar() - .ToString(), + ?.ToString() ?? "", true); //Create a fake new entry with the old name and mark as deleting @@ -1464,7 +1466,7 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER /// The ID of the fileset volume to update /// The timestamp of the operation to create /// An optional external transaction - public virtual long CreateFileset(long volumeid, DateTime timestamp, IDbTransaction transaction = null) + public virtual long CreateFileset(long volumeid, DateTime timestamp, IDbTransaction? transaction = null) { using (var tr = new TemporaryTransactionWrapper(m_connection, transaction)) using (var cmd = m_connection.CreateCommand(tr.Parent)) @@ -1495,7 +1497,7 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER /// The size of the hash /// An optional external transaction /// An enumerable of tuples containing the blocklist hash, the blocklist data and the length of the data - public IEnumerable> GetBlocklists(long volumeid, long blocksize, int hashsize, IDbTransaction transaction = null) + public IEnumerable> GetBlocklists(long volumeid, long blocksize, int hashsize, IDbTransaction? transaction = null) { using (var cmd = m_connection.CreateCommand(transaction)) { @@ -1506,9 +1508,9 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER ""B"".""Index"" >= (""A"".""Index"" * {blocksize / hashsize}) AND ""B"".""Index"" < ((""A"".""Index"" + 1) * {blocksize / hashsize}) AND ""C"".""ID"" = ""B"".""BlockID"" ORDER BY ""A"".""BlocksetID"", ""B"".""Index"""); - string curHash = null; - int count = 0; - byte[] buffer = new byte[blocksize]; + string? curHash = null; + var count = 0; + var buffer = new byte[blocksize]; using (var rd = cmd.SetCommandAndParameters(sql).SetParameterValue("@VolumeId", volumeid).ExecuteReader()) while (rd.Read()) @@ -1516,12 +1518,12 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER var blockhash = rd.GetValue(0).ToString(); if ((blockhash != curHash && curHash != null) || count + hashsize > buffer.Length) { - yield return new Tuple(curHash, buffer, count); + yield return new Tuple(curHash!, buffer, count); buffer = new byte[blocksize]; count = 0; } - var hash = Convert.FromBase64String(rd.GetValue(1).ToString()); + var hash = Convert.FromBase64String(rd.ConvertValueToString(1) ?? throw new Exception("Hash is null")); Array.Copy(hash, 0, buffer, count, hashsize); curHash = blockhash; count += hashsize; @@ -1538,7 +1540,7 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER /// Existing file set to update /// Full backup state /// An optional external transaction - public void UpdateFullBackupStateInFileset(long fileSetId, bool isFullBackup, IDbTransaction transaction = null) + public void UpdateFullBackupStateInFileset(long fileSetId, bool isFullBackup, IDbTransaction? transaction = null) { using (var tr = new TemporaryTransactionWrapper(m_connection, transaction)) using (var cmd = m_connection.CreateCommand(tr.Parent)) @@ -1589,7 +1591,7 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER /// The fileset ID /// An optional transaction /// The remote volume entry or default - public RemoteVolumeEntry GetRemoteVolumeFromFilesetID(long filesetID, IDbTransaction transaction = null) + public RemoteVolumeEntry GetRemoteVolumeFromFilesetID(long filesetID, IDbTransaction? transaction = null) { using (var cmd = m_connection.CreateCommand(transaction)) using (var rd = cmd.SetCommandAndParameters(@"SELECT ""RemoteVolume"".""ID"", ""Name"", ""Type"", ""Size"", ""Hash"", ""State"", ""DeleteGraceTime"" FROM ""RemoteVolume"", ""Fileset"" WHERE ""Fileset"".""VolumeID"" = ""RemoteVolume"".""ID"" AND ""Fileset"".""ID"" = @FilesetId") @@ -1598,12 +1600,12 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER if (rd.Read()) return new RemoteVolumeEntry( rd.ConvertValueToInt64(0, -1), - rd.GetValue(1).ToString(), - (rd.GetValue(4) == null || rd.GetValue(4) == DBNull.Value) ? null : rd.GetValue(4).ToString(), + rd.ConvertValueToString(1), + rd.ConvertValueToString(4), rd.ConvertValueToInt64(3, -1), - (RemoteVolumeType)Enum.Parse(typeof(RemoteVolumeType), rd.GetValue(2).ToString()), - (RemoteVolumeState)Enum.Parse(typeof(RemoteVolumeState), rd.GetValue(5).ToString()), - ParseFromEpochSeconds(rd.GetInt64(6)).ToLocalTime() + (RemoteVolumeType)Enum.Parse(typeof(RemoteVolumeType), rd.ConvertValueToString(2) ?? ""), + (RemoteVolumeState)Enum.Parse(typeof(RemoteVolumeState), rd.ConvertValueToString(5) ?? ""), + ParseFromEpochSeconds(rd.ConvertValueToInt64(6)).ToLocalTime() ); else return default; @@ -1752,7 +1754,7 @@ AND oldVersion.FilesetID = (SELECT ID FROM Fileset WHERE ID != @FilesetId ORDER /// The path prefix ID. /// The path to get the prefix for. /// The transaction to use for insertion, or null for no transaction - public long GetOrCreatePathPrefix(string prefix, IDbTransaction transaction) + public long GetOrCreatePathPrefix(string prefix, IDbTransaction? transaction) { // Ring-buffer style lookup for (var i = 0; i < m_pathPrefixLookup.Length; i++)