From 86b13023416c0297bbb4bef54ac8c826c68666d1 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Thu, 27 Aug 2020 17:54:08 +0300 Subject: [PATCH] Implement savepoint API for Microsoft.Data.Sqlite Closes #20228 --- global.json | 4 +- .../Microsoft.Data.Sqlite.Core.csproj | 2 +- .../SqliteTransaction.cs | 68 +++++++++++++++++++ .../SqliteTransactionTest.cs | 20 ++++++ 4 files changed, 91 insertions(+), 3 deletions(-) diff --git a/global.json b/global.json index 32b0a4b1de6..8ca80e30b53 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "tools": { - "dotnet": "5.0.100-preview.8.20417.9", + "dotnet": "5.0.100-rc.1.20427.4", "runtimes": { "dotnet": [ "3.1.7" @@ -11,7 +11,7 @@ } }, "sdk": { - "version": "5.0.100-preview.8.20417.9", + "version": "5.0.100-rc.1.20427.4", "allowPrerelease": true, "rollForward": "latestMajor" }, diff --git a/src/Microsoft.Data.Sqlite.Core/Microsoft.Data.Sqlite.Core.csproj b/src/Microsoft.Data.Sqlite.Core/Microsoft.Data.Sqlite.Core.csproj index 118b49bd201..58491395ac8 100644 --- a/src/Microsoft.Data.Sqlite.Core/Microsoft.Data.Sqlite.Core.csproj +++ b/src/Microsoft.Data.Sqlite.Core/Microsoft.Data.Sqlite.Core.csproj @@ -15,7 +15,7 @@ Microsoft.Data.Sqlite.SqliteException Microsoft.Data.Sqlite.SqliteFactory Microsoft.Data.Sqlite.SqliteParameter Microsoft.Data.Sqlite.SqliteTransaction - netstandard2.0 + netstandard2.0;net5.0 3.6 true Microsoft.Data.Sqlite.Core.ruleset diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteTransaction.cs b/src/Microsoft.Data.Sqlite.Core/SqliteTransaction.cs index cf6cb9a3201..3982a394943 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteTransaction.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteTransaction.cs @@ -113,6 +113,74 @@ public override void Rollback() RollbackInternal(); } +#if NET5_0 + /// + public override bool SupportsSavepoints => true; + + /// + public override void Save(string savepointName) + { + if (savepointName == null) + { + throw new ArgumentNullException(nameof(savepointName)); + } + + if (string.IsNullOrWhiteSpace(savepointName)) + { + throw new ArgumentException($"{nameof(savepointName)} can't be empty", nameof(savepointName)); + } + + if (_completed || _connection.State != ConnectionState.Open) + { + throw new InvalidOperationException(Resources.TransactionCompleted); + } + + _connection.ExecuteNonQuery("SAVEPOINT " + savepointName); + } + + /// + public override void Rollback(string savepointName) + { + if (savepointName == null) + { + throw new ArgumentNullException(nameof(savepointName)); + } + + if (string.IsNullOrWhiteSpace(savepointName)) + { + throw new ArgumentException($"{nameof(savepointName)} can't be empty", nameof(savepointName)); + } + + if (_completed || _connection.State != ConnectionState.Open) + { + throw new InvalidOperationException(Resources.TransactionCompleted); + } + + _connection.ExecuteNonQuery("ROLLBACK TO " + savepointName); + } + + /// + public override void Release(string savepointName) + { + if (savepointName == null) + { + throw new ArgumentNullException(nameof(savepointName)); + } + + if (string.IsNullOrWhiteSpace(savepointName)) + { + throw new ArgumentException($"{nameof(savepointName)} can't be empty", nameof(savepointName)); + } + + if (_completed || _connection.State != ConnectionState.Open) + { + throw new InvalidOperationException(Resources.TransactionCompleted); + } + + _connection.ExecuteNonQuery("RELEASE SAVEPOINT " + savepointName); + } +#endif + /// /// Releases any resources used by the transaction and rolls it back. /// diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteTransactionTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteTransactionTest.cs index 522234b0e9e..9e33003fbe5 100644 --- a/test/Microsoft.Data.Sqlite.Tests/SqliteTransactionTest.cs +++ b/test/Microsoft.Data.Sqlite.Tests/SqliteTransactionTest.cs @@ -356,6 +356,26 @@ public void Dispose_can_be_called_more_than_once() } } + [Fact] + public void Savepoint() + { + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + CreateTestTable(connection); + + var transaction = connection.BeginTransaction(); + transaction.Save("MySavepointName"); + + connection.ExecuteNonQuery("INSERT INTO TestTable (TestColumn) VALUES (8)"); + Assert.Equal(1L, connection.ExecuteScalar("SELECT COUNT(*) FROM TestTable;")); + + transaction.Rollback("MySavepointName"); + Assert.Equal(0L, connection.ExecuteScalar("SELECT COUNT(*) FROM TestTable;")); + + transaction.Release("MySavepointName"); + Assert.Throws(() => transaction.Rollback("MySavepointName")); + } + private static void CreateTestTable(SqliteConnection connection) { connection.ExecuteNonQuery(