1 | #include "catch.hpp" |
2 | #include "test_helpers.hpp" |
3 | #include "duckdb/main/appender.hpp" |
4 | |
5 | using namespace duckdb; |
6 | using namespace std; |
7 | |
8 | TEST_CASE("Test using appender after connection is gone" , "[api]" ) { |
9 | auto db = make_unique<DuckDB>(nullptr); |
10 | auto conn = make_unique<Connection>(*db); |
11 | unique_ptr<Appender> appender; |
12 | unique_ptr<QueryResult> result; |
13 | // create an appender for a non-existing table fails |
14 | REQUIRE_THROWS(make_unique<Appender>(*conn, "integers" )); |
15 | // now create the table and create the appender |
16 | REQUIRE_NO_FAIL(conn->Query("CREATE TABLE integers(i INTEGER)" )); |
17 | appender = make_unique<Appender>(*conn, "integers" ); |
18 | |
19 | // we can use the appender |
20 | appender->BeginRow(); |
21 | appender->Append<int32_t>(1); |
22 | appender->EndRow(); |
23 | |
24 | appender->Flush(); |
25 | |
26 | result = conn->Query("SELECT * FROM integers" ); |
27 | REQUIRE(CHECK_COLUMN(result, 0, {1})); |
28 | |
29 | // removing the connection invalidates the appender |
30 | conn.reset(); |
31 | REQUIRE_THROWS(appender->BeginRow()); |
32 | appender.reset(); |
33 | |
34 | // now create the appender and connection again |
35 | conn = make_unique<Connection>(*db); |
36 | appender = make_unique<Appender>(*conn, "integers" ); |
37 | |
38 | // removing the database invalidates both the connection and the appender |
39 | db.reset(); |
40 | |
41 | REQUIRE_FAIL(conn->Query("SELECT * FROM integers" )); |
42 | REQUIRE_THROWS(appender->BeginRow()); |
43 | } |
44 | |
45 | TEST_CASE("Test appender and connection destruction order" , "[api]" ) { |
46 | for (idx_t i = 0; i < 6; i++) { |
47 | auto db = make_unique<DuckDB>(nullptr); |
48 | auto con = make_unique<Connection>(*db); |
49 | REQUIRE_NO_FAIL(con->Query("CREATE TABLE integers(i INTEGER)" )); |
50 | auto appender = make_unique<Appender>(*con, "integers" ); |
51 | |
52 | switch (i) { |
53 | case 0: |
54 | // db - con - appender |
55 | db.reset(); |
56 | con.reset(); |
57 | appender.reset(); |
58 | break; |
59 | case 1: |
60 | // db - appender - con |
61 | db.reset(); |
62 | appender.reset(); |
63 | con.reset(); |
64 | break; |
65 | case 2: |
66 | // con - db - appender |
67 | con.reset(); |
68 | db.reset(); |
69 | appender.reset(); |
70 | break; |
71 | case 3: |
72 | // con - appender - db |
73 | con.reset(); |
74 | appender.reset(); |
75 | db.reset(); |
76 | break; |
77 | case 4: |
78 | // appender - con - db |
79 | appender.reset(); |
80 | con.reset(); |
81 | db.reset(); |
82 | break; |
83 | default: |
84 | // appender - db - con |
85 | appender.reset(); |
86 | db.reset(); |
87 | con.reset(); |
88 | break; |
89 | } |
90 | } |
91 | } |
92 | |
93 | TEST_CASE("Test using appender after table is dropped" , "[api]" ) { |
94 | DuckDB db(nullptr); |
95 | Connection con(db); |
96 | // create the table |
97 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)" )); |
98 | // now create the appender |
99 | Appender appender(con, "integers" ); |
100 | |
101 | // appending works initially |
102 | appender.BeginRow(); |
103 | appender.Append<int32_t>(1); |
104 | appender.EndRow(); |
105 | appender.Flush(); |
106 | |
107 | // now drop the table |
108 | REQUIRE_NO_FAIL(con.Query("DROP TABLE integers" )); |
109 | // now appending fails |
110 | appender.BeginRow(); |
111 | appender.Append<int32_t>(1); |
112 | appender.EndRow(); |
113 | REQUIRE_THROWS(appender.Flush()); |
114 | } |
115 | |
116 | TEST_CASE("Test using appender after table is altered" , "[api]" ) { |
117 | DuckDB db(nullptr); |
118 | Connection con(db); |
119 | // create the table |
120 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)" )); |
121 | // now create the appender |
122 | Appender appender(con, "integers" ); |
123 | |
124 | // appending works initially |
125 | appender.BeginRow(); |
126 | appender.Append<int32_t>(1); |
127 | appender.EndRow(); |
128 | appender.Flush(); |
129 | |
130 | // now create a new table with the same name but different types |
131 | REQUIRE_NO_FAIL(con.Query("DROP TABLE integers" )); |
132 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i VARCHAR)" )); |
133 | // now appending fails |
134 | appender.BeginRow(); |
135 | appender.Append<int32_t>(1); |
136 | appender.EndRow(); |
137 | REQUIRE_THROWS(appender.Flush()); |
138 | } |
139 | |
140 | TEST_CASE("Test appenders and transactions" , "[api]" ) { |
141 | DuckDB db(nullptr); |
142 | Connection con(db); |
143 | unique_ptr<QueryResult> result; |
144 | // create the table |
145 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)" )); |
146 | // now create the appender |
147 | Appender appender(con, "integers" ); |
148 | |
149 | // rollback an append |
150 | REQUIRE_NO_FAIL(con.Query("BEGIN TRANSACTION" )); |
151 | appender.BeginRow(); |
152 | appender.Append<int32_t>(1); |
153 | appender.EndRow(); |
154 | appender.Flush(); |
155 | result = con.Query("SELECT * FROM integers" ); |
156 | REQUIRE(CHECK_COLUMN(result, 0, {1})); |
157 | |
158 | REQUIRE_NO_FAIL(con.Query("ROLLBACK" )); |
159 | result = con.Query("SELECT * FROM integers" ); |
160 | REQUIRE(CHECK_COLUMN(result, 0, {})); |
161 | |
162 | // we can still use the appender in auto commit mode |
163 | appender.BeginRow(); |
164 | appender.Append<int32_t>(1); |
165 | appender.EndRow(); |
166 | appender.Flush(); |
167 | |
168 | result = con.Query("SELECT * FROM integers" ); |
169 | REQUIRE(CHECK_COLUMN(result, 0, {1})); |
170 | } |
171 | |
172 | TEST_CASE("Test using multiple appenders" , "[api]" ) { |
173 | DuckDB db(nullptr); |
174 | Connection con(db); |
175 | unique_ptr<QueryResult> result; |
176 | // create the table |
177 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE t1(i INTEGER)" )); |
178 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE t2(i VARCHAR, j DATE)" )); |
179 | // now create the appender |
180 | Appender a1(con, "t1" ); |
181 | Appender a2(con, "t2" ); |
182 | |
183 | // begin appending from both |
184 | REQUIRE_NO_FAIL(con.Query("BEGIN TRANSACTION" )); |
185 | a1.BeginRow(); |
186 | a1.Append<int32_t>(1); |
187 | a1.EndRow(); |
188 | a1.Flush(); |
189 | |
190 | a2.BeginRow(); |
191 | a2.Append<const char *>("hello" ); |
192 | a2.Append<Value>(Value::DATE(1992, 1, 1)); |
193 | a2.EndRow(); |
194 | a2.Flush(); |
195 | |
196 | result = con.Query("SELECT * FROM t1" ); |
197 | REQUIRE(CHECK_COLUMN(result, 0, {1})); |
198 | result = con.Query("SELECT * FROM t2" ); |
199 | REQUIRE(CHECK_COLUMN(result, 0, {"hello" })); |
200 | REQUIRE(CHECK_COLUMN(result, 1, {Value::DATE(1992, 1, 1)})); |
201 | |
202 | REQUIRE_NO_FAIL(con.Query("ROLLBACK" )); |
203 | result = con.Query("SELECT * FROM t1" ); |
204 | REQUIRE(CHECK_COLUMN(result, 0, {})); |
205 | } |
206 | |
207 | TEST_CASE("Test usage of appender interleaved with connection usage" , "[api]" ) { |
208 | DuckDB db(nullptr); |
209 | Connection con(db); |
210 | unique_ptr<QueryResult> result; |
211 | // create the table |
212 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE t1(i INTEGER)" )); |
213 | Appender appender(con, "t1" ); |
214 | |
215 | appender.AppendRow(1); |
216 | appender.Flush(); |
217 | |
218 | result = con.Query("SELECT * FROM t1" ); |
219 | REQUIRE(CHECK_COLUMN(result, 0, {1})); |
220 | |
221 | appender.AppendRow(2); |
222 | appender.Flush(); |
223 | |
224 | result = con.Query("SELECT * FROM t1" ); |
225 | REQUIRE(CHECK_COLUMN(result, 0, {1, 2})); |
226 | } |
227 | |