forked from Giorgi/DuckDB.NET
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTransactionTests.cs
More file actions
154 lines (135 loc) · 5.42 KB
/
Copy pathTransactionTests.cs
File metadata and controls
154 lines (135 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
using System;
using System.Data;
using DuckDB.NET.Data;
using FluentAssertions;
using Xunit;
namespace DuckDB.NET.Test;
public class TransactionTests(DuckDBDatabaseFixture db) : DuckDBTestBase(db)
{
[Fact]
public void SimpleTransactionTest()
{
Command.CommandText = "CREATE TABLE transactionUsers (id INTEGER, name TEXT);";
Command.ExecuteNonQuery();
object rowsInTable;
using (var transaction = Connection.BeginTransaction(IsolationLevel.Snapshot))
{
transaction.IsolationLevel.Should().Be(IsolationLevel.Snapshot);
transaction.Connection.Should().Be(Connection);
Command.CommandText = "INSERT INTO transactionUsers VALUES (1, 'user1'), (2, 'user2')";
Command.ExecuteNonQuery();
Command.CommandText = "SELECT count(*) FROM transactionUsers";
rowsInTable = Command.ExecuteScalar();
rowsInTable.Should().Be(2);
transaction.Commit();
}
Command.CommandText = "SELECT count(*) FROM transactionUsers";
rowsInTable = Command.ExecuteScalar();
rowsInTable.Should().Be(2);
using (Connection.BeginTransaction())
{
Command.CommandText = "INSERT INTO transactionUsers VALUES (3, 'user3'), (4, 'user4')";
Command.ExecuteNonQuery();
Command.CommandText = "SELECT count(*) FROM transactionUsers";
rowsInTable = Command.ExecuteScalar();
rowsInTable.Should().Be(4);
}
Command.CommandText = "SELECT count(*) FROM transactionUsers";
rowsInTable = Command.ExecuteScalar();
rowsInTable.Should().Be(2);
}
[Fact]
public void ParallelTransactionsTest()
{
using (Connection.BeginTransaction())
{
Connection
.Invoking(con => con.BeginTransaction())
.Should().Throw<InvalidOperationException>();
}
}
[Fact]
public void CommitTransactionTwiceTest()
{
using (var transaction = Connection.BeginTransaction())
{
transaction.Commit();
transaction.Invoking(tr => tr.Commit())
.Should().Throw<InvalidOperationException>();
}
}
[Fact]
public void RollbackAndCommitTransactionTest()
{
using (var transaction = Connection.BeginTransaction())
{
transaction.Rollback();
transaction.Invoking(tr => tr.Commit())
.Should().Throw<InvalidOperationException>();
}
}
[Fact]
public void RollbackTransactionTwiceTest()
{
using (var transaction = Connection.BeginTransaction())
{
transaction.Rollback();
transaction.Invoking(tr => tr.Rollback())
.Should().Throw<InvalidOperationException>();
}
}
[Fact]
public void CommitAndRollbackTransactionTest()
{
using (var transaction = Connection.BeginTransaction())
{
transaction.Commit();
transaction.Invoking(tr => tr.Rollback())
.Should().Throw<InvalidOperationException>();
}
}
[Fact]
public void TransactionInvalidStateTest()
{
Connection.Close();
Connection.Invoking(connection => connection.BeginTransaction()).Should().Throw<InvalidOperationException>();
Connection.Open();
Connection.Invoking(connection => connection.BeginTransaction(IsolationLevel.Serializable)).Should()
.Throw<ArgumentException>();
}
[Fact]
public void AbortedTransactionTest()
{
// This block of code is to make the transaction commit fail using an index limitation in duckdb
// (https://github.com/duckdb/duckdb/issues/17802)
Command.CommandText = "CREATE TABLE IF NOT EXISTS test_table (id INTEGER PRIMARY KEY);";
Command.ExecuteNonQuery();
Command.CommandText = "INSERT OR IGNORE INTO test_table VALUES (1);";
Command.ExecuteNonQuery();
// Keep a reference to the row in an open transaction to trigger the concurrent access limitation
using var tx1 = Connection.BeginTransaction();
Command.Transaction = tx1;
Command.CommandText = "SELECT id FROM test_table LIMIT 1;";
using var reader1 = Command.ExecuteReader();
using var conn2 = Connection.Duplicate();
conn2.Open();
using var cmd2 = conn2.CreateCommand();
cmd2.CommandText = "UPDATE test_table SET id = 1 WHERE id = 1;";
cmd2.ExecuteNonQuery();
var tx2 = conn2.BeginTransaction();
cmd2.Transaction = tx2;
cmd2.CommandText = "UPDATE test_table SET id = 1 WHERE id = 1;";
cmd2.ExecuteNonQuery();
using (new FluentAssertions.Execution.AssertionScope())
{
// Check that when the transaction commit fails and the transaction
// enters an aborted state, the transaction and connection objects
// remain in the expected state.
tx2.Invoking(tx2 => tx2.Commit()).Should().Throw<DuckDBException>().Where(ex => ex.ErrorType == Native.DuckDBErrorType.Transaction);
tx2.Invoking(tx2 => tx2.Commit()).Should().Throw<InvalidOperationException>();
tx2.Invoking(tx2 => tx2.Rollback()).Should().Throw<InvalidOperationException>();
tx2.Invoking(tx2 => tx2.Dispose()).Should().NotThrow();
conn2.Invoking(conn2 => conn2.BeginTransaction()).Should().NotThrow();
}
}
}