1#include "sqlite_transfer.hpp"
2#include "duckdb/common/types.hpp"
3#include "duckdb.hpp"
4#include "sqlite3.h"
5#include "catch.hpp"
6#include "test_helpers.hpp"
7
8#include <string>
9#include <sstream>
10
11TEST_CASE("Pass nullptr to transfer function", "[dbtransfer]") {
12 duckdb::DuckDB source_db(nullptr);
13 duckdb::Connection con(source_db);
14
15 REQUIRE_FALSE(sqlite::TransferDatabase(con, nullptr));
16}
17
18TEST_CASE("Check transfer from DuckDB to sqlite database", "[dbtransfer]") {
19 duckdb::DuckDB source_db(nullptr);
20 duckdb::Connection con(source_db);
21
22 REQUIRE_NO_FAIL(con.Query("CREATE TABLE items(field1 VARCHAR, field2 INTEGER)"));
23 REQUIRE_NO_FAIL(con.Query("INSERT INTO items VALUES ('a', 1)"));
24 REQUIRE_NO_FAIL(con.Query("INSERT INTO items VALUES ('b', 2)"));
25 REQUIRE_NO_FAIL(con.Query("INSERT INTO items VALUES ('c', 3)"));
26 REQUIRE_NO_FAIL(con.Query("INSERT INTO items VALUES ('d', 4)"));
27 REQUIRE_NO_FAIL(con.Query("INSERT INTO items VALUES ('e', 5)"));
28
29 sqlite3 *destination_db = nullptr;
30 if (sqlite3_open(":memory:", &destination_db) != SQLITE_OK) {
31 REQUIRE(false);
32 return;
33 }
34
35 REQUIRE(sqlite::TransferDatabase(con, destination_db));
36
37 const char *sql = "SELECT field1, field2 FROM items";
38 auto check_callback = [](void *unused, int argc, char **values, char **columns) {
39 static char field1 = 'a';
40 static char field2 = '1';
41 unused = 0;
42
43 REQUIRE(strcmp(columns[0], "field1") == 0);
44 REQUIRE(strcmp(columns[1], "field2") == 0);
45 char f1[2] = {field1, '\0'};
46 REQUIRE(strcmp(values[0], f1) == 0);
47 char f2[2] = {field2, '\0'};
48 REQUIRE(strcmp(values[1], f2) == 0);
49
50 ++field1;
51 ++field2;
52
53 return 0;
54 };
55 char *err = 0;
56
57 if (sqlite3_exec(destination_db, sql, check_callback, 0, &err) != SQLITE_OK) {
58 sqlite3_free(err);
59 sqlite3_close(destination_db);
60
61 REQUIRE(false);
62 return;
63 }
64
65 sqlite3_close(destination_db);
66}
67
68TEST_CASE("Pass pointer to sqlite3 as nullptr to QueryDatabase", "[dbtransfer]") {
69 duckdb::vector<duckdb::SQLType> result_types = {duckdb::SQLTypeId::VARCHAR, duckdb::SQLTypeId::INTEGER};
70 std::string query = "SELECT * from items";
71 int interrupt = 0;
72 REQUIRE_FALSE(sqlite::QueryDatabase(result_types, nullptr, std::move(query), interrupt));
73}
74
75TEST_CASE("Check getting query from sqlite database", "[dbtransfer]") {
76 sqlite3 *source_db = nullptr;
77 if (sqlite3_open(":memory:", &source_db) != SQLITE_OK) {
78 REQUIRE(false);
79 return;
80 }
81
82 const char *sql_create_table = "CREATE TABLE items (field1 VARCHAR, field2 INTEGER)";
83 auto empty_callback = [](void *, int, char **, char **) { return 0; };
84 char *err = 0;
85 if (sqlite3_exec(source_db, sql_create_table, empty_callback, 0, &err) != SQLITE_OK) {
86 sqlite3_free(err);
87 sqlite3_close(source_db);
88
89 REQUIRE(false);
90 return;
91 }
92
93 // Inserting 5 rows in sqlite db
94 char field1 = 'a';
95 char field2 = '1';
96 for (int i = 0; i < 5; ++i, ++field1, ++field2) {
97 std::ostringstream sql_insert;
98 sql_insert << "INSERT INTO items VALUES ('" << field1 << "', " << field2 << ")";
99 if (sqlite3_exec(source_db, sql_insert.str().c_str(), empty_callback, 0, &err) != SQLITE_OK) {
100 sqlite3_free(err);
101 sqlite3_close(source_db);
102
103 REQUIRE(false);
104 return;
105 }
106 }
107
108 duckdb::vector<duckdb::SQLType> result_types = {duckdb::SQLTypeId::VARCHAR, duckdb::SQLTypeId::INTEGER};
109 std::string query = "SELECT * from items";
110 int interrupt = 0;
111 auto result = sqlite::QueryDatabase(result_types, source_db, std::move(query), interrupt);
112
113 sqlite3_close(source_db);
114
115 REQUIRE(result->success);
116 REQUIRE(CHECK_COLUMN(result, 0, {"a", "b", "c", "d", "e"}));
117 REQUIRE(CHECK_COLUMN(result, 1, {1, 2, 3, 4, 5}));
118}
119