1 | #include "catch.hpp" |
2 | #include "duckdb/common/file_system.hpp" |
3 | #include "test_helpers.hpp" |
4 | #include "duckdb/storage/storage_info.hpp" |
5 | |
6 | using namespace duckdb; |
7 | using namespace std; |
8 | |
9 | TEST_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 | |
75 | TEST_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 | |