Skip to content

Commit 0bd4938

Browse files
committed
fix issues with cloning and transactions. Also fix an old crash that triggers when a table has only one (key) column.
1 parent 4df36e0 commit 0bd4938

5 files changed

Lines changed: 84 additions & 58 deletions

File tree

SQLite.Net.Tests/InsertTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public void InsertAllFailureInsideTransaction()
118118
var testObjects = Enumerable.Range(1, 20).Select(i => new UniqueObj { Id = i }).ToList();
119119
testObjects[^1].Id = 1; // causes the insert to fail because of duplicate key
120120

121-
ExceptionAssert.Throws<SQLiteException>(() => _db.RunInTransaction(() => { _db.InsertAll(testObjects, false); }));
121+
ExceptionAssert.Throws<SQLiteException>(() => _db.RunInTransaction(db => { db.InsertAll(testObjects, false); }));
122122
Assert.AreEqual(0, _db.Table<UniqueObj>().Count());
123123
}
124124

@@ -165,7 +165,7 @@ public void InsertAllFailureSucceedsOutsideTransaction()
165165
public void InsertAllSuccessInsideTransaction()
166166
{
167167
var testObjects = Enumerable.Range(1, 20).Select(i => new UniqueObj { Id = i }).ToList();
168-
_db.RunInTransaction(() => { _db.InsertAll(testObjects); });
168+
_db.RunInTransaction(db => { db.InsertAll(testObjects); });
169169

170170
Assert.AreEqual(testObjects.Count, _db.Table<UniqueObj>().Count());
171171
}

SQLite.Net.Tests/SQLite.Net2.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
<TargetFramework>netcoreapp3.1</TargetFramework>
55

66
<IsPackable>false</IsPackable>
7+
8+
<LangVersion>latest</LangVersion>
79
</PropertyGroup>
810

911
<ItemGroup>

SQLite.Net.Tests/TransactionTest.cs

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public class TestObj
3535
{
3636
[AutoIncrement, PrimaryKey]
3737
public int Id { get; set; }
38+
39+
public int Toto { get; set; }
3840

3941
public override string ToString()
4042
{
@@ -55,28 +57,42 @@ public TestDb(String path)
5557
}
5658
}
5759

60+
[Test]
61+
public void SuccesfulNestedSavepointTransaction()
62+
{
63+
db.RunInTransaction(dbb =>
64+
{
65+
dbb.Delete(testObjects[0]);
66+
dbb.RunInTransaction(dbbb => { dbbb.InsertAll(new TestObj[] { new(), new() }); });
67+
});
68+
69+
//catch (SQLiteException e) when(e.Message == "database is locked")
70+
Assert.AreEqual(testObjects.Count+1, db.Table<TestObj>().Count());
71+
}
72+
5873
[Test]
5974
public void FailNestedSavepointTransaction()
6075
{
76+
var hasCatch = false;
6177
try
6278
{
63-
db.RunInTransaction(() =>
79+
db.RunInTransaction(dbb =>
6480
{
65-
db.Delete(testObjects[0]);
81+
dbb.Delete(testObjects[0]);
6682

67-
db.RunInTransaction(() =>
83+
dbb.RunInTransaction(dbbb =>
6884
{
69-
db.Delete(testObjects[1]);
70-
85+
dbbb.Delete(testObjects[1]);
7186
throw new TransactionTestException();
7287
});
7388
});
7489
}
7590
catch (TransactionTestException)
7691
{
77-
// ignore
92+
hasCatch = true;
7893
}
7994

95+
Assert.IsTrue(hasCatch);
8096
Assert.AreEqual(testObjects.Count, db.Table<TestObj>().Count());
8197
}
8298

@@ -85,9 +101,9 @@ public void FailSavepointTransaction()
85101
{
86102
try
87103
{
88-
db.RunInTransaction(() =>
104+
db.RunInTransaction(dbb =>
89105
{
90-
db.Delete(testObjects[0]);
106+
dbb.Delete(testObjects[0]);
91107

92108
throw new TransactionTestException();
93109
});
@@ -103,11 +119,11 @@ public void FailSavepointTransaction()
103119
[Test]
104120
public void SuccessfulNestedSavepointTransaction()
105121
{
106-
db.RunInTransaction(() =>
122+
db.RunInTransaction(dbb =>
107123
{
108-
db.Delete(testObjects[0]);
124+
dbb.Delete(testObjects[0]);
109125

110-
db.RunInTransaction(() => { db.Delete(testObjects[1]); });
126+
dbb.RunInTransaction(dbbb => { dbbb.Delete(testObjects[1]); });
111127
});
112128

113129
Assert.AreEqual(testObjects.Count - 2, db.Table<TestObj>().Count());
@@ -116,11 +132,11 @@ public void SuccessfulNestedSavepointTransaction()
116132
[Test]
117133
public void SuccessfulSavepointTransaction()
118134
{
119-
db.RunInTransaction(() =>
135+
db.RunInTransaction(dbb =>
120136
{
121-
db.Delete(testObjects[0]);
122-
db.Delete(testObjects[1]);
123-
db.Insert(new TestObj());
137+
dbb.Delete(testObjects[0]);
138+
dbb.Delete(testObjects[1]);
139+
dbb.Insert(new TestObj());
124140
});
125141

126142
Assert.AreEqual(testObjects.Count - 1, db.Table<TestObj>().Count());
@@ -132,7 +148,7 @@ public void FailSavepointTransactionException()
132148
{
133149
try
134150
{
135-
db.RunInTransaction(() =>
151+
db.RunInTransaction(dbb =>
136152
{
137153
throw new TransactionTestException();
138154
});
@@ -149,9 +165,9 @@ public void FailSavepointTransactionException()
149165
[Test]
150166
public void SuccesfulSavepointTransaction()
151167
{
152-
db.RunInTransaction(() =>
168+
db.RunInTransaction(dbb =>
153169
{
154-
db.InsertOrReplaceAll(testObjects);
170+
dbb.InsertOrReplaceAll(testObjects);
155171
});
156172
}
157173
}

nuget/pack.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ if ($IsMacOS) {
77
$msbuild = join-path $msbuild 'MSBuild\Current\Bin\MSBuild.exe'
88
}
99
$version="2.1.0"
10-
$versionSuffix="-pre6"
10+
$versionSuffix="-pre7"
1111

1212
#####################
1313
#Build release config
@@ -17,4 +17,4 @@ del *.nupkg
1717
if ($lastexitcode -ne 0) { exit $lastexitcode; }
1818

1919
nuget push "sqlite-net2.$version$versionSuffix.nupkg" -Source $nugetServer
20-
#copy "sqlite-net2.$version$versionSuffix.nupkg" "D:\repos\localnugets"
20+
copy "sqlite-net2.$version$versionSuffix.nupkg" "D:\repos\localnugets"

src/SQLite.Net/SQLiteConnection.cs

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,7 @@ private void RollbackTo(string? savepoint, bool noThrow)
984984
else
985985
{
986986
if (IsInTransaction)
987-
DoSavePointExecute(savepoint!, "rollback to ");
987+
DoSavePointExecute(savepoint!, "rollback to ", true);
988988
else
989989
transactionDepth = 0;
990990
}
@@ -1009,9 +1009,9 @@ private void RollbackTo(string? savepoint, bool noThrow)
10091009
/// All transactions methods creates a state in this connection. Be sure to not share it with other calls.
10101010
/// </remarks>
10111011
public void Release(string savepoint)
1012-
=> DoSavePointExecute(savepoint, "release ");
1012+
=> DoSavePointExecute(savepoint, "release ", false);
10131013

1014-
private void DoSavePointExecute(string savePoint, string cmd)
1014+
private void DoSavePointExecute(string savePoint, string cmd, bool isRollback)
10151015
{
10161016
// Validate the savepoint
10171017
var firstLen = savePoint?.IndexOf('D') ?? 0;
@@ -1021,19 +1021,22 @@ private void DoSavePointExecute(string savePoint, string cmd)
10211021
if (!int.TryParse(savePoint.Substring(firstLen + 1), out var depth) || depth < 0)
10221022
throw new ArgumentException($"savePoint '{savePoint}' is not valid, and should be the result of a call to SaveTransactionPoint.", nameof(savePoint));
10231023

1024-
if (depth == 1)
1025-
{
1026-
RollbackTo(null, true);
1027-
return;
1028-
}
1024+
// if (depth == 1)
1025+
// {
1026+
// if (isRollback)
1027+
// {
1028+
// RollbackTo(null, true);
1029+
// }
1030+
// return;
1031+
// }
10291032

10301033
if (depth > transactionDepth)
10311034
throw new ArgumentException($"savePoint '{savePoint}' is not valid: depth ({depth}) >= transactionDepth ({transactionDepth})", nameof(savePoint));
10321035

10331036
try
10341037
{
1035-
transactionDepth = depth-1;
10361038
Execute(cmd + savePoint);
1039+
transactionDepth = depth-1;
10371040
}
10381041
catch
10391042
{
@@ -1046,28 +1049,37 @@ private void DoSavePointExecute(string savePoint, string cmd)
10461049

10471050
/// <summary>
10481051
/// Executes
1049-
/// <paramref name="action" />
1052+
/// <paramref name="writeAction" />
10501053
/// within a (possibly nested) transaction by wrapping it in a SAVEPOINT. If an
10511054
/// exception occurs the whole transaction is rolled back, not just the current savepoint. The exception
10521055
/// is rethrown.
10531056
/// </summary>
1054-
/// <param name="action">
1055-
/// The <see cref="Action" /> to perform within a transaction.
1056-
/// <paramref name="action" />
1057-
/// can contain any number
1058-
/// of operations on the connection but should never call <see cref="BeginTransaction" /> or
1059-
/// <see cref="Commit" />.
1057+
/// <param name="writeAction">
1058+
/// The <see cref="Action" /> to perform within a transaction.
1059+
/// <paramref name="writeAction" />
1060+
/// can contain any number of operations on the connection but should never call <see cref="BeginTransaction" /> or <see cref="Commit" />.
10601061
/// </param>
1062+
/// <param name="cloneConnection">clone the connection if not already in a transaction, to prevent sharing a transaction with other simultaneous calls that do not use a transaction.</param>
10611063
/// <remarks>
1064+
/// When not in a transaction, create a new connection to execute the write action so it is not shared with other calls.
1065+
///
10621066
/// All transactions methods creates a state in this connection. Be sure to not share it with other calls.
10631067
/// </remarks>
1064-
public void RunInTransaction(Action action)
1068+
public void RunInTransaction(Action<SQLiteConnection> writeAction, bool cloneConnection = true)
10651069
{
1070+
//When not in a transaction, create a new connection to execute the write action
1071+
if (cloneConnection && !IsInTransaction)
1072+
{
1073+
using var db = Clone();
1074+
db.RunInTransaction(writeAction, false);
1075+
return;
1076+
}
1077+
10661078
string? savePoint = null;
10671079
try
10681080
{
10691081
savePoint = SaveTransactionPoint();
1070-
action();
1082+
writeAction(this);
10711083
Release(savePoint);
10721084
}
10731085
catch (Exception e)
@@ -1096,11 +1108,9 @@ public int InsertAll(IEnumerable objects, bool runInTransaction = true)
10961108
var c = 0;
10971109
if (runInTransaction)
10981110
{
1099-
using var db = Clone();
1100-
db.RunInTransaction(() =>
1111+
RunInTransaction(db =>
11011112
{
1102-
foreach (var r in objects)
1103-
c += db.Insert(r);
1113+
c = db.InsertAll(objects, false);
11041114
});
11051115
}
11061116
else
@@ -1132,8 +1142,7 @@ public int InsertAll(IEnumerable objects, string extra, bool runInTransaction =
11321142
var c = 0;
11331143
if (runInTransaction)
11341144
{
1135-
using var db = Clone();
1136-
db.RunInTransaction(() =>
1145+
RunInTransaction(db =>
11371146
{
11381147
foreach (var r in objects)
11391148
c += db.Insert(r, extra);
@@ -1168,8 +1177,7 @@ public int InsertAll(IEnumerable objects, Type objType, bool runInTransaction =
11681177
var c = 0;
11691178
if (runInTransaction)
11701179
{
1171-
using var db = Clone();
1172-
db.RunInTransaction(() =>
1180+
RunInTransaction(db =>
11731181
{
11741182
foreach (var r in objects)
11751183
c += db.Insert(r, objType);
@@ -1240,8 +1248,7 @@ public int InsertOrReplace(object? obj)
12401248
public int InsertOrReplaceAll(IEnumerable objects)
12411249
{
12421250
var c = 0;
1243-
using var db = Clone();
1244-
db.RunInTransaction(() =>
1251+
RunInTransaction(db =>
12451252
{
12461253
foreach (var r in objects)
12471254
c += db.InsertOrReplace(r);
@@ -1306,8 +1313,7 @@ public int InsertOrReplace(object obj, Type objType)
13061313
public int InsertOrReplaceAll(IEnumerable objects, Type objType)
13071314
{
13081315
var c = 0;
1309-
using var db = Clone();
1310-
db.RunInTransaction(() =>
1316+
RunInTransaction(db =>
13111317
{
13121318
foreach (var r in objects)
13131319
c += db.InsertOrReplace(r, objType);
@@ -1492,10 +1498,13 @@ public int Update(object obj, Type objType)
14921498
throw new NotSupportedException("Cannot update " + map.TableName + ": it has no PK");
14931499
}
14941500

1495-
var cols = from p in map.Columns
1501+
var cols = (from p in map.Columns
14961502
where !(from pkey in map.PKs
14971503
select pkey).Contains(p)
1498-
select p;
1504+
select p).ToList();
1505+
1506+
if (cols.Count == 0)
1507+
return 0;
14991508

15001509
var vals = from c in cols
15011510
select c.GetValue(obj);
@@ -1515,9 +1524,9 @@ public int Update(object obj, Type objType)
15151524
}
15161525
else
15171526
{
1518-
q = string.Format("update \"{0}\" set {1} where {2} = ? ", map.TableName,
1527+
q = string.Format("update \"{0}\" set {1} where {2} ", map.TableName,
15191528
string.Join(",", (from c in cols select "\"" + c.Name + "\" = ? ").ToArray()),
1520-
pk.Name);
1529+
map.PkWhereSql);
15211530
}
15221531

15231532
try
@@ -1556,8 +1565,7 @@ public int UpdateAll(IEnumerable objects, bool runInTransaction = true)
15561565
var c = 0;
15571566
if (runInTransaction)
15581567
{
1559-
using var db = Clone();
1560-
db.RunInTransaction(() =>
1568+
RunInTransaction(db =>
15611569
{
15621570
foreach (var r in objects)
15631571
c += db.Update(r);

0 commit comments

Comments
 (0)