Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Microsoft.Data.Sqlite: Add Default Timeout connection string keyword #23091

Merged
merged 2 commits into from
Dec 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public override string DataSource
/// <seealso href="https://docs.microsoft.com/dotnet/standard/data/sqlite/database-errors">Database Errors</seealso>
public virtual int DefaultTimeout
{
get => _defaultTimeout ?? 30;
get => _defaultTimeout ?? ConnectionOptions.DefaultTimeout;
set => _defaultTimeout = value;
}

Expand Down
36 changes: 32 additions & 4 deletions src/Microsoft.Data.Sqlite.Core/SqliteConnectionStringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -36,7 +38,8 @@ private enum Keywords
Cache,
Password,
ForeignKeys,
RecursiveTriggers
RecursiveTriggers,
DefaultTimeout
}

private static readonly IReadOnlyList<string> _validKeywords;
Expand All @@ -48,30 +51,34 @@ 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<string, Keywords>(8, StringComparer.OrdinalIgnoreCase)
_keywords = new Dictionary<string, Keywords>(10, StringComparer.OrdinalIgnoreCase)
{
[DataSourceKeyword] = Keywords.DataSource,
[ModeKeyword] = Keywords.Mode,
[CacheKeyword] = Keywords.Cache,
[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
};
}

Expand Down Expand Up @@ -185,6 +192,16 @@ public bool RecursiveTriggers
set => base[RecursiveTriggersKeyword] = _recursiveTriggers = value;
}

/// <summary>
/// Gets or sets the default <see cref="SqliteConnection.DefaultTimeout" /> value.
/// </summary>
/// <value>The default <see cref="SqliteConnection.DefaultTimeout" /> value.</value>
public int DefaultTimeout
{
get => _defaultTimeout;
set => base[DefaultTimeoutKeyword] = _defaultTimeout = value;
}

/// <summary>
/// Gets or sets the value associated with the specified key.
/// </summary>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
50 changes: 48 additions & 2 deletions test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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()
{
Expand Down Expand Up @@ -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<string>)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]
Expand All @@ -132,7 +176,7 @@ public void Values_works()
var values = (ICollection<object>)new SqliteConnectionStringBuilder().Values;

Assert.True(values.IsReadOnly);
Assert.Equal(6, values.Count);
Assert.Equal(7, values.Count);
}

[Fact]
Expand Down Expand Up @@ -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();

Expand All @@ -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]
Expand Down Expand Up @@ -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());
}

Expand Down
18 changes: 13 additions & 5 deletions test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -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())
Expand All @@ -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);
}
}
Expand Down