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 | #include <thread> |
11 | |
12 | using namespace duckdb; |
13 | using namespace std; |
14 | |
15 | TEST_CASE("Test that sequence never returns the same value twice even with aborts" , "[persistence][.]" ) { |
16 | FileSystem fs; |
17 | |
18 | string dbdir = TestCreatePath("defaultseq" ); |
19 | DeleteDatabase(dbdir); |
20 | // create a database |
21 | { |
22 | DuckDB db(dbdir); |
23 | Connection con(db); |
24 | REQUIRE_NO_FAIL(con.Query("CREATE SEQUENCE seq" )); |
25 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE a (i INTEGER DEFAULT nextval('seq'), j INTEGER)" )); |
26 | } |
27 | |
28 | // now fork the process a bunch of times |
29 | for (int i = 0; i < 100; i++) { |
30 | // fork the process |
31 | pid_t pid = fork(); |
32 | if (pid == 0) { |
33 | // child process, connect to the database and start inserting values |
34 | DuckDB db(dbdir); |
35 | Connection con(db); |
36 | while (true) { |
37 | con.Query("INSERT INTO a (j) VALUES(1)" ); |
38 | } |
39 | } else if (pid > 0) { |
40 | // parent process, sleep a bit |
41 | usleep(100000); |
42 | // send SIGKILL to the child |
43 | if (kill(pid, SIGKILL) != 0) { |
44 | FAIL(); |
45 | } |
46 | // sleep a bit before continuing the loop, to make sure the lock on the database was released |
47 | usleep(100000); |
48 | } else { |
49 | FAIL(); |
50 | } |
51 | } |
52 | // now connect to the database |
53 | { |
54 | DuckDB db(dbdir); |
55 | Connection con(db); |
56 | // verify that "i" only has unique values from the sequence |
57 | // i.e. COUNT = COUNT(DISTINCT) |
58 | auto result = con.Query("SELECT COUNT(i), COUNT(DISTINCT i) FROM a" ); |
59 | REQUIRE(CHECK_COLUMN(result, 1, {result->GetValue(0, 0)})); |
60 | } |
61 | DeleteDatabase(dbdir); |
62 | } |
63 | |
64 | static void write_entries_to_table(DuckDB *db, int i) { |
65 | Connection con(*db); |
66 | if (i % 2 == 0) { |
67 | // i % 2 = 0, insert values |
68 | while (true) { |
69 | con.Query("INSERT INTO a (j) VALUES(1)" ); |
70 | } |
71 | } else { |
72 | // use nextval in select clause |
73 | while (true) { |
74 | con.Query("SELECT nextval('seq')" ); |
75 | } |
76 | } |
77 | } |
78 | |
79 | TEST_CASE("Test that sequence never returns the same value twice even with aborts and concurrent usage" , |
80 | "[persistence][.]" ) { |
81 | FileSystem fs; |
82 | |
83 | string dbdir = TestCreatePath("defaultseqconcurrent" ); |
84 | DeleteDatabase(dbdir); |
85 | // create a database |
86 | { |
87 | DuckDB db(dbdir); |
88 | Connection con(db); |
89 | REQUIRE_NO_FAIL(con.Query("CREATE SEQUENCE seq" )); |
90 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE a (i INTEGER DEFAULT nextval('seq'), j INTEGER)" )); |
91 | } |
92 | // now fork the process a bunch of times |
93 | for (int i = 0; i < 100; i++) { |
94 | // fork the process |
95 | pid_t pid = fork(); |
96 | if (pid == 0) { |
97 | // child process, connect to the database and start inserting values in separate threads |
98 | DuckDB db(dbdir); |
99 | thread write_threads[8]; |
100 | for (size_t i = 0; i < 8; i++) { |
101 | write_threads[i] = thread(write_entries_to_table, &db, i); |
102 | } |
103 | while (true) |
104 | ; |
105 | } else if (pid > 0) { |
106 | // parent process, sleep a bit |
107 | usleep(100000); |
108 | // send SIGKILL to the child |
109 | if (kill(pid, SIGKILL) != 0) { |
110 | FAIL(); |
111 | } |
112 | // sleep a bit before continuing the loop, to make sure the lock on the database was released |
113 | usleep(100000); |
114 | } else { |
115 | FAIL(); |
116 | } |
117 | } |
118 | // now connect to the database |
119 | { |
120 | DuckDB db(dbdir); |
121 | Connection con(db); |
122 | // verify that "i" only has unique values from the sequence |
123 | // i.e. COUNT = COUNT(DISTINCT) |
124 | auto result = con.Query("SELECT COUNT(i), COUNT(DISTINCT i) FROM a" ); |
125 | REQUIRE(CHECK_COLUMN(result, 1, {result->GetValue(0, 0)})); |
126 | } |
127 | DeleteDatabase(dbdir); |
128 | } |
129 | |