| 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 | |