1#include "catch.hpp"
2#include "test_helpers.hpp"
3#include "duckdb/main/appender.hpp"
4
5using namespace duckdb;
6using namespace std;
7
8TEST_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
45TEST_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
93TEST_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
116TEST_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
140TEST_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
172TEST_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
207TEST_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