1 | #include "catch.hpp" |
2 | #include "duckdb/common/file_system.hpp" |
3 | #include "duckdb.hpp" |
4 | #include "duckdb/main/appender.hpp" |
5 | #include "test_helpers.hpp" |
6 | |
7 | #include <signal.h> |
8 | #include <sys/mman.h> |
9 | #include <unistd.h> |
10 | |
11 | using namespace duckdb; |
12 | using namespace std; |
13 | |
14 | #define BOOL_COUNT 3 |
15 | |
16 | TEST_CASE("Test write lock with multiple processes" , "[persistence][.]" ) { |
17 | uint64_t *count = |
18 | (uint64_t *)mmap(NULL, sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0); |
19 | *count = 0; |
20 | |
21 | string dbdir = TestCreatePath("writelocktest" ); |
22 | DeleteDatabase(dbdir); |
23 | // test write lock |
24 | // fork away a child |
25 | pid_t pid = fork(); |
26 | if (pid == 0) { |
27 | // child process |
28 | // open db for writing |
29 | DuckDB db(dbdir); |
30 | Connection con(db); |
31 | // opened db for writing |
32 | // insert some values |
33 | (*count)++; |
34 | con.Query("CREATE TABLE a(i INTEGER)" ); |
35 | con.Query("INSERT INTO a VALUES(42)" ); |
36 | while (true) { |
37 | con.Query("SELECT * FROM a" ); |
38 | usleep(100); |
39 | } |
40 | } else if (pid > 0) { |
41 | unique_ptr<DuckDB> db; |
42 | // parent process |
43 | // sleep a bit to wait for child process |
44 | while (*count == 0) { |
45 | usleep(100); |
46 | } |
47 | // try to open db for writing, this should fail |
48 | REQUIRE_THROWS(db = make_unique<DuckDB>(dbdir)); |
49 | // kill the child |
50 | if (kill(pid, SIGKILL) != 0) { |
51 | FAIL(); |
52 | } |
53 | } |
54 | } |
55 | |
56 | TEST_CASE("Test read lock with multiple processes" , "[persistence][.]" ) { |
57 | uint64_t *count = |
58 | (uint64_t *)mmap(NULL, sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0); |
59 | *count = 0; |
60 | |
61 | string dbdir = TestCreatePath("readlocktest" ); |
62 | DeleteDatabase(dbdir); |
63 | |
64 | // create the database |
65 | { |
66 | DuckDB db(dbdir); |
67 | Connection con(db); |
68 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE a(i INTEGER)" )); |
69 | REQUIRE_NO_FAIL(con.Query("INSERT INTO a VALUES (42)" )); |
70 | } |
71 | // test read lock |
72 | pid_t pid = fork(); |
73 | DBConfig config; |
74 | config.access_mode = AccessMode::READ_ONLY; |
75 | if (pid == 0) { |
76 | // child process |
77 | // open db for reading |
78 | DuckDB db(dbdir, &config); |
79 | Connection con(db); |
80 | |
81 | (*count)++; |
82 | // query some values |
83 | con.Query("SELECT i+2 FROM a" ); |
84 | while (true) { |
85 | usleep(100); |
86 | con.Query("SELECT * FROM a" ); |
87 | } |
88 | } else if (pid > 0) { |
89 | unique_ptr<DuckDB> db; |
90 | // parent process |
91 | // sleep a bit to wait for child process |
92 | while (*count == 0) { |
93 | usleep(100); |
94 | } |
95 | // try to open db for writing, this should fail |
96 | REQUIRE_THROWS(db = make_unique<DuckDB>(dbdir)); |
97 | // but opening db for reading should work |
98 | REQUIRE_NOTHROW(db = make_unique<DuckDB>(dbdir, &config)); |
99 | // we can query the database |
100 | Connection con(*db); |
101 | REQUIRE_NO_FAIL(con.Query("SELECT * FROM a" )); |
102 | // kill the child |
103 | if (kill(pid, SIGKILL) != 0) { |
104 | FAIL(); |
105 | } |
106 | } |
107 | } |
108 | |