1#include "catch.hpp"
2#include "duckdb/common/file_system.hpp"
3#include "test_helpers.hpp"
4#include "duckdb/storage/storage_info.hpp"
5
6using namespace duckdb;
7using namespace std;
8
9TEST_CASE("Test abort of commit with persistent storage", "[storage]") {
10 unique_ptr<MaterializedQueryResult> result;
11 auto storage_database = TestCreatePath("storage_test");
12 auto config = GetTestConfig();
13
14 Value expected_count, expected_count_b;
15 Value expected_sum_a, expected_sum_b, expected_sum_strlen;
16
17 // make sure the database does not exist
18 DeleteDatabase(storage_database);
19 {
20 // create a database and insert values
21 DuckDB db(storage_database, config.get());
22 Connection con(db), con2(db);
23 REQUIRE_NO_FAIL(con.Query("CREATE TABLE test (a INTEGER PRIMARY KEY, b INTEGER, c VARCHAR);"));
24 REQUIRE_NO_FAIL(con.Query(
25 "INSERT INTO test VALUES (11, 22, 'hello'), (13, 22, 'world'), (12, 21, 'test'), (10, NULL, NULL)"));
26
27 // start a transaction for con and con2
28 REQUIRE_NO_FAIL(con.Query("BEGIN TRANSACTION"));
29 REQUIRE_NO_FAIL(con2.Query("BEGIN TRANSACTION"));
30
31 // insert the value 14 in both transactions
32 REQUIRE_NO_FAIL(con.Query("INSERT INTO test VALUES (14, 10, 'con')"));
33 REQUIRE_NO_FAIL(con2.Query("INSERT INTO test VALUES (15, 10, 'con2')"));
34 REQUIRE_NO_FAIL(con2.Query("INSERT INTO test VALUES (14, 10, 'con2')"));
35
36 // commit both
37 // con2 will fail
38 REQUIRE_NO_FAIL(con.Query("COMMIT"));
39 REQUIRE_FAIL(con2.Query("COMMIT"));
40
41 // now add another row of all NULLs
42 REQUIRE_NO_FAIL(con.Query("INSERT INTO test VALUES (15, NULL, NULL)"));
43
44 // check that the result is correct
45 expected_count = Value::BIGINT(6);
46 expected_count_b = Value::BIGINT(4);
47 expected_sum_a = Value::BIGINT(10 + 11 + 12 + 13 + 14 + 15);
48 expected_sum_b = Value::BIGINT(22 + 22 + 21 + 10);
49 expected_sum_strlen = Value::BIGINT(5 + 5 + 4 + 3);
50
51 // verify the contents of the database
52 result = con.Query("SELECT COUNT(*), COUNT(a), COUNT(b), SUM(a), SUM(b), SUM(LENGTH(c)) FROM test");
53 REQUIRE(CHECK_COLUMN(result, 0, {expected_count}));
54 REQUIRE(CHECK_COLUMN(result, 1, {expected_count}));
55 REQUIRE(CHECK_COLUMN(result, 2, {expected_count_b}));
56 REQUIRE(CHECK_COLUMN(result, 3, {expected_sum_a}));
57 REQUIRE(CHECK_COLUMN(result, 4, {expected_sum_b}));
58 REQUIRE(CHECK_COLUMN(result, 5, {expected_sum_strlen}));
59 }
60 // reload the database from disk
61 for (idx_t i = 0; i < 2; i++) {
62 DuckDB db(storage_database, config.get());
63 Connection con(db);
64 result = con.Query("SELECT COUNT(*), COUNT(a), COUNT(b), SUM(a), SUM(b), SUM(LENGTH(c)) FROM test");
65 REQUIRE(CHECK_COLUMN(result, 0, {expected_count}));
66 REQUIRE(CHECK_COLUMN(result, 1, {expected_count}));
67 REQUIRE(CHECK_COLUMN(result, 2, {expected_count_b}));
68 REQUIRE(CHECK_COLUMN(result, 3, {expected_sum_a}));
69 REQUIRE(CHECK_COLUMN(result, 4, {expected_sum_b}));
70 REQUIRE(CHECK_COLUMN(result, 5, {expected_sum_strlen}));
71 }
72 DeleteDatabase(storage_database);
73}
74
75TEST_CASE("Test abort of large commit with persistent storage", "[storage][.]") {
76 unique_ptr<MaterializedQueryResult> result;
77 auto storage_database = TestCreatePath("storage_test");
78 auto config = GetTestConfig();
79
80 Value expected_count, expected_count_b;
81 Value expected_sum_a, expected_sum_b, expected_sum_strlen;
82
83 // make sure the database does not exist
84 DeleteDatabase(storage_database);
85 {
86 // create a database and insert values
87 DuckDB db(storage_database, config.get());
88 Connection con(db), con2(db);
89 REQUIRE_NO_FAIL(con.Query("CREATE TABLE test (a INTEGER PRIMARY KEY, b INTEGER, c VARCHAR);"));
90 REQUIRE_NO_FAIL(con.Query(
91 "INSERT INTO test VALUES (11, 22, 'hello'), (13, 22, 'world'), (12, 21, 'test'), (10, NULL, NULL)"));
92
93 // start a transaction for con and con2
94 REQUIRE_NO_FAIL(con.Query("BEGIN TRANSACTION"));
95 REQUIRE_NO_FAIL(con2.Query("BEGIN TRANSACTION"));
96
97 // insert the value 14 in con
98 REQUIRE_NO_FAIL(con.Query("INSERT INTO test VALUES (14, 10, 'con')"));
99 // now insert many non-conflicting values in con2
100 idx_t tpl_count = 2 * Storage::BLOCK_SIZE / sizeof(int);
101 for (int i = 0; i < (int)tpl_count; i++) {
102 REQUIRE_NO_FAIL(con2.Query("INSERT INTO test VALUES (" + to_string(15 + i) + ", 10, 'con2')"));
103 }
104 // finally insert one conflicting tuple
105 REQUIRE_NO_FAIL(con2.Query("INSERT INTO test VALUES (14, 10, 'con2')"));
106
107 // commit both
108 // con2 will fail
109 REQUIRE_NO_FAIL(con.Query("COMMIT"));
110 REQUIRE_FAIL(con2.Query("COMMIT"));
111
112 // now add another row of all NULLs
113 REQUIRE_NO_FAIL(con.Query("INSERT INTO test VALUES (15, NULL, NULL)"));
114
115 // check that the result is correct
116 expected_count = Value::BIGINT(6);
117 expected_count_b = Value::BIGINT(4);
118 expected_sum_a = Value::BIGINT(10 + 11 + 12 + 13 + 14 + 15);
119 expected_sum_b = Value::BIGINT(22 + 22 + 21 + 10);
120 expected_sum_strlen = Value::BIGINT(5 + 5 + 4 + 3);
121
122 // verify the contents of the database
123 result = con.Query("SELECT COUNT(*), COUNT(a), COUNT(b), SUM(a), SUM(b), SUM(LENGTH(c)) FROM test");
124 REQUIRE(CHECK_COLUMN(result, 0, {expected_count}));
125 REQUIRE(CHECK_COLUMN(result, 1, {expected_count}));
126 REQUIRE(CHECK_COLUMN(result, 2, {expected_count_b}));
127 REQUIRE(CHECK_COLUMN(result, 3, {expected_sum_a}));
128 REQUIRE(CHECK_COLUMN(result, 4, {expected_sum_b}));
129 REQUIRE(CHECK_COLUMN(result, 5, {expected_sum_strlen}));
130 }
131 // reload the database from disk
132 for (idx_t i = 0; i < 2; i++) {
133 DuckDB db(storage_database, config.get());
134 Connection con(db);
135 result = con.Query("SELECT COUNT(*), COUNT(a), COUNT(b), SUM(a), SUM(b), SUM(LENGTH(c)) FROM test");
136 REQUIRE(CHECK_COLUMN(result, 0, {expected_count}));
137 REQUIRE(CHECK_COLUMN(result, 1, {expected_count}));
138 REQUIRE(CHECK_COLUMN(result, 2, {expected_count_b}));
139 REQUIRE(CHECK_COLUMN(result, 3, {expected_sum_a}));
140 REQUIRE(CHECK_COLUMN(result, 4, {expected_sum_b}));
141 REQUIRE(CHECK_COLUMN(result, 5, {expected_sum_strlen}));
142 }
143 DeleteDatabase(storage_database);
144}
145