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
11using namespace duckdb;
12using namespace std;
13
14#define BOOL_COUNT 3
15
16TEST_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
56TEST_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