diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs index 1337127eac8..a456763751c 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs @@ -130,7 +130,7 @@ public override string DataSource /// Database Errors public virtual int DefaultTimeout { - get => _defaultTimeout ?? 30; + get => _defaultTimeout ?? ConnectionOptions.DefaultTimeout; set => _defaultTimeout = value; } diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteConnectionStringBuilder.cs b/src/Microsoft.Data.Sqlite.Core/SqliteConnectionStringBuilder.cs index e19252c2365..c54d8597bb5 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteConnectionStringBuilder.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteConnectionStringBuilder.cs @@ -28,6 +28,8 @@ public class SqliteConnectionStringBuilder : DbConnectionStringBuilder private const string PasswordKeyword = "Password"; private const string ForeignKeysKeyword = "Foreign Keys"; private const string RecursiveTriggersKeyword = "Recursive Triggers"; + private const string DefaultTimeoutKeyword = "Default Timeout"; + private const string CommandTimeoutKeyword = "Command Timeout"; private enum Keywords { @@ -36,7 +38,8 @@ private enum Keywords Cache, Password, ForeignKeys, - RecursiveTriggers + RecursiveTriggers, + DefaultTimeout } private static readonly IReadOnlyList _validKeywords; @@ -48,19 +51,21 @@ private enum Keywords private string _password = string.Empty; private bool? _foreignKeys; private bool _recursiveTriggers; + private int _defaultTimeout = 30; static SqliteConnectionStringBuilder() { - var validKeywords = new string[6]; + var validKeywords = new string[7]; validKeywords[(int)Keywords.DataSource] = DataSourceKeyword; validKeywords[(int)Keywords.Mode] = ModeKeyword; validKeywords[(int)Keywords.Cache] = CacheKeyword; validKeywords[(int)Keywords.Password] = PasswordKeyword; validKeywords[(int)Keywords.ForeignKeys] = ForeignKeysKeyword; validKeywords[(int)Keywords.RecursiveTriggers] = RecursiveTriggersKeyword; + validKeywords[(int)Keywords.DefaultTimeout] = DefaultTimeoutKeyword; _validKeywords = validKeywords; - _keywords = new Dictionary(8, StringComparer.OrdinalIgnoreCase) + _keywords = new Dictionary(10, StringComparer.OrdinalIgnoreCase) { [DataSourceKeyword] = Keywords.DataSource, [ModeKeyword] = Keywords.Mode, @@ -68,10 +73,12 @@ static SqliteConnectionStringBuilder() [PasswordKeyword] = Keywords.Password, [ForeignKeysKeyword] = Keywords.ForeignKeys, [RecursiveTriggersKeyword] = Keywords.RecursiveTriggers, + [DefaultTimeoutKeyword] = Keywords.DefaultTimeout, // aliases [FilenameKeyword] = Keywords.DataSource, - [DataSourceNoSpaceKeyword] = Keywords.DataSource + [DataSourceNoSpaceKeyword] = Keywords.DataSource, + [CommandTimeoutKeyword] = Keywords.DefaultTimeout }; } @@ -185,6 +192,16 @@ public bool RecursiveTriggers set => base[RecursiveTriggersKeyword] = _recursiveTriggers = value; } + /// + /// Gets or sets the default value. + /// + /// The default value. + public int DefaultTimeout + { + get => _defaultTimeout; + set => base[DefaultTimeoutKeyword] = _defaultTimeout = value; + } + /// /// Gets or sets the value associated with the specified key. /// @@ -230,6 +247,10 @@ public override object? this[string keyword] RecursiveTriggers = Convert.ToBoolean(value, CultureInfo.InvariantCulture); return; + case Keywords.DefaultTimeout: + DefaultTimeout = Convert.ToInt32(value); + return; + default: Debug.Assert(false, "Unexpected keyword: " + keyword); return; @@ -372,6 +393,9 @@ public override bool TryGetValue(string keyword, out object? value) case Keywords.RecursiveTriggers: return RecursiveTriggers; + case Keywords.DefaultTimeout: + return DefaultTimeout; + default: Debug.Assert(false, "Unexpected keyword: " + index); return null; @@ -411,6 +435,10 @@ private void Reset(Keywords index) _recursiveTriggers = false; return; + case Keywords.DefaultTimeout: + _defaultTimeout = 30; + return; + default: Debug.Assert(false, "Unexpected keyword: " + index); return; diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs index a403fdd4489..50f845304e5 100644 --- a/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs +++ b/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs @@ -20,7 +20,6 @@ public void Ctor_sets_values() { using (var connection = new SqliteConnection("Data Source=:memory:")) { - connection.DefaultTimeout = 1; connection.Open(); using (var transaction = connection.BeginTransaction()) @@ -29,7 +28,6 @@ public void Ctor_sets_values() Assert.Equal("SELECT 1;", command.CommandText); Assert.Same(connection, command.Connection); - Assert.Equal(1, command.CommandTimeout); Assert.Same(transaction, command.Transaction); } } @@ -75,6 +73,54 @@ public void CommandText_throws_when_set_when_open_reader() } } + [Fact] + public void CommandTimeout_works() + { + var command = new SqliteCommand + { + Connection = new SqliteConnection("Command Timeout=1") + { + DefaultTimeout = 2 + }, + CommandTimeout = 3 + }; + + Assert.Equal(3, command.CommandTimeout); + } + + [Fact] + public void CommandTimeout_defaults_to_connection() + { + var command = new SqliteCommand + { + Connection = new SqliteConnection("Default Timeout=1") + { + DefaultTimeout = 2 + } + }; + + Assert.Equal(2, command.CommandTimeout); + } + + [Fact] + public void CommandTimeout_defaults_to_connection_string() + { + var command = new SqliteCommand + { + Connection = new SqliteConnection("Default Timeout=1") + }; + + Assert.Equal(1, command.CommandTimeout); + } + + [Fact] + public void CommandTimeout_defaults_to_30() + { + var command = new SqliteCommand(); + + Assert.Equal(30, command.CommandTimeout); + } + [Fact] public void Connection_can_be_unset() { diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionStringBuilderTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionStringBuilderTest.cs index b52bfbb4bdd..6c5feffbfb0 100644 --- a/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionStringBuilderTest.cs +++ b/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionStringBuilderTest.cs @@ -10,10 +10,13 @@ namespace Microsoft.Data.Sqlite { public class SqliteConnectionStringBuilderTest { - [Fact] - public void Ctor_parses_options() + [Theory] + [InlineData("Data Source")] + [InlineData("Filename")] + [InlineData("DataSource")] + public void Ctor_parses_DataSource(string keyword) { - var builder = new SqliteConnectionStringBuilder("Data Source=test.db"); + var builder = new SqliteConnectionStringBuilder($"{keyword}=test.db"); Assert.Equal("test.db", builder.DataSource); } @@ -33,6 +36,40 @@ public void Ctor_parses_mode() Assert.Equal(SqliteOpenMode.Memory, builder.Mode); } + [Fact] + public void Ctor_parses_Password() + { + var builder = new SqliteConnectionStringBuilder("Password=key"); + + Assert.Equal("key", builder.Password); + } + + [Fact] + public void Ctor_parses_ForeignKeys() + { + var builder = new SqliteConnectionStringBuilder("Foreign Keys=True"); + + Assert.True(builder.ForeignKeys); + } + + [Fact] + public void Ctor_parses_RecursiveTriggers() + { + var builder = new SqliteConnectionStringBuilder("Recursive Triggers=True"); + + Assert.True(builder.RecursiveTriggers); + } + + [Theory] + [InlineData("Default Timeout")] + [InlineData("Command Timeout")] + public void Ctor_parses_DefaultTimeout(string keyword) + { + var builder = new SqliteConnectionStringBuilder($"{keyword}=1"); + + Assert.Equal(1, builder.DefaultTimeout); + } + [Fact] public void ConnectionString_defaults_to_empty() { @@ -111,19 +148,26 @@ public void Password_defaults_to_empty() Assert.Empty(new SqliteConnectionStringBuilder().Password); } + [Fact] + public void DefaultTimeout_defaults_to_30() + { + Assert.Equal(30, new SqliteConnectionStringBuilder().DefaultTimeout); + } + [Fact] public void Keys_works() { var keys = (ICollection)new SqliteConnectionStringBuilder().Keys; Assert.True(keys.IsReadOnly); - Assert.Equal(6, keys.Count); + Assert.Equal(7, keys.Count); Assert.Contains("Data Source", keys); Assert.Contains("Mode", keys); Assert.Contains("Cache", keys); Assert.Contains("Password", keys); Assert.Contains("Foreign Keys", keys); Assert.Contains("Recursive Triggers", keys); + Assert.Contains("Default Timeout", keys); } [Fact] @@ -132,7 +176,7 @@ public void Values_works() var values = (ICollection)new SqliteConnectionStringBuilder().Values; Assert.True(values.IsReadOnly); - Assert.Equal(6, values.Count); + Assert.Equal(7, values.Count); } [Fact] @@ -235,7 +279,7 @@ public void Item_throws_when_cannot_convert_to_bool_on_set(object value) public void Clear_resets_everything() { var builder = new SqliteConnectionStringBuilder( - "Data Source=test.db;Mode=Memory;Cache=Shared;Password=test;Foreign Keys=True;Recursive Triggers=True"); + "Data Source=test.db;Mode=Memory;Cache=Shared;Password=test;Foreign Keys=True;Recursive Triggers=True;Default Timeout=1"); builder.Clear(); @@ -245,6 +289,7 @@ public void Clear_resets_everything() Assert.Empty(builder.Password); Assert.Null(builder.ForeignKeys); Assert.False(builder.RecursiveTriggers); + Assert.Equal(30, builder.DefaultTimeout); } [Fact] @@ -326,11 +371,12 @@ public void ToString_builds_string() Mode = SqliteOpenMode.Memory, Password = "test", ForeignKeys = true, - RecursiveTriggers = true + RecursiveTriggers = true, + DefaultTimeout = 1 }; Assert.Equal( - "Data Source=test.db;Mode=Memory;Cache=Shared;Password=test;Foreign Keys=True;Recursive Triggers=True", + "Data Source=test.db;Mode=Memory;Cache=Shared;Password=test;Foreign Keys=True;Recursive Triggers=True;Default Timeout=1", builder.ToString()); } diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs index 531c7a35585..ca63d75c09b 100644 --- a/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs +++ b/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs @@ -107,14 +107,24 @@ public void DefaultTimeout_defaults_to_30() } [Fact] - public void DefaultTimeout_works() + public void DefaultTimeout_defaults_to_connection_string() { - var connection = new SqliteConnection(); - connection.DefaultTimeout = 1; + var connection = new SqliteConnection("Default Timeout=1"); Assert.Equal(1, connection.DefaultTimeout); } + [Fact] + public void DefaultTimeout_works() + { + var connection = new SqliteConnection("Default Timeout=1") + { + DefaultTimeout = 2 + }; + + Assert.Equal(2, connection.DefaultTimeout); + } + [Fact] public void ServerVersion_returns_value() { @@ -548,7 +558,6 @@ public void CreateCommand_returns_command() { using (var connection = new SqliteConnection("Data Source=:memory:")) { - connection.DefaultTimeout = 1; connection.Open(); using (var transaction = connection.BeginTransaction()) @@ -557,7 +566,6 @@ public void CreateCommand_returns_command() Assert.NotNull(command); Assert.Same(connection, command.Connection); - Assert.Equal(1, command.CommandTimeout); Assert.Same(transaction, command.Transaction); } }