Skip to content

Commit a818c6f

Browse files
Use RAII for the appender and results
1 parent 8a69cad commit a818c6f

6 files changed

Lines changed: 197 additions & 78 deletions

File tree

include/sqlgen/duckdb/Connection.hpp

Lines changed: 23 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
#include "../internal/to_container.hpp"
2323
#include "../is_connection.hpp"
2424
#include "./parsing/Parser_default.hpp"
25+
#include "DuckDBAppender.hpp"
2526
#include "DuckDBConnection.hpp"
27+
#include "DuckDBResult.hpp"
2628
#include "Iterator.hpp"
2729
#include "sqlgen/dynamic/Operation.hpp"
2830
#include "sqlgen/dynamic/SelectFrom.hpp"
@@ -39,11 +41,7 @@ class Connection {
3941
static rfl::Result<Ref<Connection>> make(
4042
const std::optional<std::string> &_fname) noexcept;
4143

42-
~Connection() {
43-
if (appender_) {
44-
duckdb_appender_destroy(appender_.get());
45-
}
46-
}
44+
~Connection() = default;
4745

4846
Result<Nothing> begin_transaction() noexcept;
4947

@@ -63,31 +61,20 @@ class Connection {
6361
transform([](const auto &_str) { return _str.c_str(); }));
6462

6563
return get_duckdb_logical_types(_insert_stmt.table, _insert_stmt.columns)
66-
.and_then([&](auto _types) -> Result<Nothing> {
67-
duckdb_appender appender{};
68-
if (duckdb_appender_create_query(
69-
conn_->conn(), sql.c_str(),
70-
static_cast<idx_t>(_insert_stmt.columns.size()),
71-
_types.data(), "sqlgen_appended_data", columns.data(),
72-
&appender) == DuckDBError) {
73-
return error("Could not create appender.");
74-
}
75-
const auto res = write_to_appender(_begin, _end, appender);
76-
duckdb_appender_destroy(&appender);
77-
return res;
64+
.and_then([&](const auto &_types) {
65+
return DuckDBAppender::make(sql, conn_, columns, _types);
66+
})
67+
.and_then([&](const auto &_appender) {
68+
return write_to_appender(_begin, _end, _appender->appender());
7869
});
7970
}
8071

8172
template <class ContainerType>
8273
Result<ContainerType> read(const dynamic::SelectFrom &_query) {
8374
using ValueType = transpilation::value_t<ContainerType>;
84-
auto res = Ref<duckdb_result>();
85-
duckdb_query(conn_->conn(), to_sql(_query).c_str(), res.get());
8675
const auto result =
8776
internal::to_container<ContainerType, Iterator<ValueType>>(
88-
Iterator<ValueType>(res, conn_));
89-
// TODO: Destroy result inside of iterator.
90-
duckdb_destroy_result(res.get());
77+
Iterator<ValueType>(to_sql(_query), conn_));
9178
return result;
9279
}
9380

@@ -112,15 +99,11 @@ class Connection {
11299
const auto sql = to_sql(_write_stmt);
113100

114101
return get_duckdb_logical_types(_write_stmt.table, _write_stmt.columns)
115-
.and_then([&](auto _types) -> Result<Nothing> {
116-
appender_ = std::make_unique<duckdb_appender>();
117-
if (duckdb_appender_create_query(
118-
conn_->conn(), sql.c_str(),
119-
static_cast<idx_t>(_write_stmt.columns.size()), _types.data(),
120-
"sqlgen_appended_data", columns.data(),
121-
appender_.get()) == DuckDBError) {
122-
return error("Could not create appender.");
123-
}
102+
.and_then([&](auto _types) {
103+
return DuckDBAppender::make(sql, conn_, columns, _types);
104+
})
105+
.transform([&](auto &&_appender) {
106+
appender_ = _appender.ptr();
124107
return Nothing{};
125108
});
126109
}
@@ -129,7 +112,6 @@ class Connection {
129112
if (!appender_) {
130113
return error("No write operation in progress - nothing to end.");
131114
}
132-
duckdb_appender_destroy(appender_.get());
133115
appender_ = nullptr;
134116
return Nothing{};
135117
}
@@ -139,7 +121,7 @@ class Connection {
139121
if (!appender_) {
140122
return error("No write operation in progress - nothing to write.");
141123
}
142-
return write_to_appender(_begin, _end, *appender_);
124+
return write_to_appender(_begin, _end, appender_->appender());
143125
}
144126

145127
private:
@@ -158,24 +140,13 @@ class Connection {
158140
const auto select_from = dynamic::SelectFrom{
159141
.table_or_query = _table, .fields = fields, .limit = dynamic::Limit{0}};
160142

161-
duckdb_result res{};
162-
163-
const auto state =
164-
duckdb_query(conn_->conn(), to_sql(select_from).c_str(), &res);
165-
166-
if (state == DuckDBError) {
167-
const auto err = error(duckdb_result_error(&res));
168-
duckdb_destroy_result(&res);
169-
return err;
170-
}
171-
172-
const auto types = internal::collect::vector(
173-
iota(static_cast<idx_t>(0), static_cast<idx_t>(fields.size())) |
174-
transform(std::bind_front(duckdb_column_logical_type, &res)));
175-
176-
duckdb_destroy_result(&res);
177-
178-
return types;
143+
return DuckDBResult::make(to_sql(select_from), conn_)
144+
.transform([&](const auto &_res) {
145+
return internal::collect::vector(
146+
iota(static_cast<idx_t>(0), static_cast<idx_t>(fields.size())) |
147+
transform(
148+
std::bind_front(duckdb_column_logical_type, &_res->res())));
149+
});
179150
}
180151

181152
template <class ItBegin, class ItEnd>
@@ -210,7 +181,7 @@ class Connection {
210181

211182
private:
212183
/// The appender to be used for the write statements
213-
std::unique_ptr<duckdb_appender> appender_;
184+
std::shared_ptr<DuckDBAppender> appender_;
214185

215186
/// The underlying duckdb3 connection.
216187
ConnPtr conn_;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#ifndef SQLGEN_DUCKDB_DUCKDBAPPENDER_HPP_
2+
#define SQLGEN_DUCKDB_DUCKDBAPPENDER_HPP_
3+
4+
#include <duckdb.h>
5+
6+
#include <string>
7+
8+
#include "DuckDBConnection.hpp"
9+
10+
namespace sqlgen::duckdb {
11+
12+
class DuckDBAppender {
13+
using ConnPtr = Ref<DuckDBConnection>;
14+
15+
public:
16+
static Result<Ref<DuckDBAppender>> make(
17+
const std::string& _sql, const ConnPtr& _conn,
18+
const std::vector<const char*>& _columns,
19+
const std::vector<duckdb_logical_type>& _types) {
20+
try {
21+
return Ref<DuckDBAppender>::make(_sql, _conn, _columns, _types);
22+
} catch (const std::exception& e) {
23+
return error(e.what());
24+
}
25+
}
26+
27+
DuckDBAppender(const std::string& _sql, const ConnPtr& _conn,
28+
std::vector<const char*> _columns,
29+
std::vector<duckdb_logical_type> _types)
30+
: destroy_(false) {
31+
if (duckdb_appender_create_query(
32+
_conn->conn(), _sql.c_str(), static_cast<idx_t>(_columns.size()),
33+
_types.data(), "sqlgen_appended_data", _columns.data(),
34+
&appender_) == DuckDBError) {
35+
throw std::runtime_error("Could not create appender.");
36+
}
37+
destroy_ = true;
38+
}
39+
40+
~DuckDBAppender() {
41+
if (destroy_) {
42+
duckdb_appender_destroy(&appender_);
43+
}
44+
}
45+
46+
DuckDBAppender(const DuckDBAppender& _other) = delete;
47+
48+
DuckDBAppender(DuckDBAppender&& _other)
49+
: destroy_(_other.destroy_), appender_(_other.appender_) {
50+
_other.destroy_ = false;
51+
}
52+
53+
DuckDBAppender& operator=(const DuckDBAppender& _other) = delete;
54+
55+
DuckDBAppender& operator=(DuckDBAppender&& _other) {
56+
if (this == &_other) {
57+
return *this;
58+
}
59+
destroy_ = _other.destroy_;
60+
appender_ = _other.appender_;
61+
_other.destroy_ = false;
62+
return *this;
63+
}
64+
65+
duckdb_appender& appender() { return appender_; }
66+
67+
private:
68+
bool destroy_;
69+
70+
duckdb_appender appender_;
71+
};
72+
73+
} // namespace sqlgen::duckdb
74+
75+
#endif
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#ifndef SQLGEN_DUCKDB_DUCKDBRESULT_HPP_
2+
#define SQLGEN_DUCKDB_DUCKDBRESULT_HPP_
3+
4+
#include <duckdb.h>
5+
6+
#include <string>
7+
8+
#include "DuckDBConnection.hpp"
9+
10+
namespace sqlgen::duckdb {
11+
12+
class DuckDBResult {
13+
using ConnPtr = Ref<DuckDBConnection>;
14+
15+
public:
16+
static Result<Ref<DuckDBResult>> make(const std::string& _query,
17+
const ConnPtr& _conn) {
18+
try {
19+
return Ref<DuckDBResult>::make(_query, _conn);
20+
} catch (const std::exception& e) {
21+
return error(e.what());
22+
}
23+
}
24+
25+
DuckDBResult(const std::string& _query, const ConnPtr& _conn)
26+
: destroy_(false) {
27+
if (duckdb_query(_conn->conn(), _query.c_str(), &res_) == DuckDBError) {
28+
throw std::runtime_error(duckdb_result_error(&res_));
29+
}
30+
destroy_ = true;
31+
}
32+
33+
~DuckDBResult() {
34+
if (destroy_) {
35+
duckdb_destroy_result(&res_);
36+
}
37+
}
38+
39+
DuckDBResult(const DuckDBResult& _other) = delete;
40+
41+
DuckDBResult(DuckDBResult&& _other)
42+
: destroy_(_other.destroy_), res_(_other.res_) {
43+
_other.destroy_ = false;
44+
}
45+
46+
DuckDBResult& operator=(const DuckDBResult& _other) = delete;
47+
48+
DuckDBResult& operator=(DuckDBResult&& _other) {
49+
if (this == &_other) {
50+
return *this;
51+
}
52+
destroy_ = _other.destroy_;
53+
res_ = _other.res_;
54+
_other.destroy_ = false;
55+
return *this;
56+
}
57+
58+
duckdb_result& res() { return res_; }
59+
60+
private:
61+
bool destroy_;
62+
63+
duckdb_result res_;
64+
};
65+
66+
} // namespace sqlgen::duckdb
67+
68+
#endif

include/sqlgen/duckdb/Iterator.hpp

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33

44
#include <duckdb.h>
55

6-
#include <optional>
76
#include <rfl.hpp>
87
#include <string>
98
#include <vector>
109

1110
#include "../Ref.hpp"
1211
#include "../Result.hpp"
1312
#include "DuckDBConnection.hpp"
13+
#include "DuckDBResult.hpp"
1414
#include "from_chunk_ptrs.hpp"
1515
#include "make_chunk_ptrs.hpp"
1616

@@ -19,7 +19,7 @@ namespace sqlgen::duckdb {
1919
template <class T>
2020
class Iterator {
2121
using ConnPtr = Ref<DuckDBConnection>;
22-
using ResultPtr = Ref<duckdb_result>;
22+
using ResultPtr = Ref<DuckDBResult>;
2323

2424
public:
2525
struct End {
@@ -36,10 +36,10 @@ class Iterator {
3636
using difference_type = std::ptrdiff_t;
3737
using value_type = Result<T>;
3838

39-
Iterator(const ResultPtr& _res, const ConnPtr& _conn)
40-
: res_(_res),
39+
Iterator(const std::string& _query, const ConnPtr& _conn)
40+
: res_(DuckDBResult::make(_query, _conn)),
4141
conn_(_conn),
42-
current_batch_(get_next_batch(_res, _conn)),
42+
current_batch_(get_next_batch(res_, _conn)),
4343
ix_(0) {}
4444

4545
~Iterator() = default;
@@ -67,19 +67,22 @@ class Iterator {
6767

6868
private:
6969
static Ref<std::vector<Result<T>>> get_next_batch(
70-
const ResultPtr& _res, const ConnPtr& _conn) noexcept {
71-
duckdb_data_chunk chunk = duckdb_fetch_chunk(*_res);
72-
if (!chunk) {
73-
return Ref<std::vector<Result<T>>>::make();
74-
}
75-
const idx_t row_count = duckdb_data_chunk_get_size(chunk);
76-
return make_chunk_ptrs<T>(_res, chunk)
77-
.transform([&](auto&& _chunk_ptrs) {
78-
auto batch = Ref<std::vector<Result<T>>>::make();
79-
for (idx_t i = 0; i < row_count; ++i) {
80-
batch->emplace_back(from_chunk_ptrs<T>(_chunk_ptrs, i));
70+
const Result<ResultPtr>& _result_ptr, const ConnPtr& _conn) noexcept {
71+
return _result_ptr
72+
.and_then([&](const auto& _res) -> Result<Ref<std::vector<Result<T>>>> {
73+
duckdb_data_chunk chunk = duckdb_fetch_chunk(_res->res());
74+
if (!chunk) {
75+
return Ref<std::vector<Result<T>>>::make();
8176
}
82-
return batch;
77+
const idx_t row_count = duckdb_data_chunk_get_size(chunk);
78+
return make_chunk_ptrs<T>(_res, chunk)
79+
.transform([&](auto&& _chunk_ptrs) {
80+
auto batch = Ref<std::vector<Result<T>>>::make();
81+
for (idx_t i = 0; i < row_count; ++i) {
82+
batch->emplace_back(from_chunk_ptrs<T>(_chunk_ptrs, i));
83+
}
84+
return batch;
85+
});
8386
})
8487
.or_else([](auto _err) {
8588
return Ref<std::vector<Result<T>>>::make(
@@ -90,7 +93,7 @@ class Iterator {
9093

9194
private:
9295
/// The underlying DuckDB result.
93-
ResultPtr res_;
96+
Result<ResultPtr> res_;
9497

9598
/// The underlying connection.
9699
ConnPtr conn_;

0 commit comments

Comments
 (0)