Refactored the write and read function (#61)

This commit is contained in:
Dr. Patrick Urbanke (劉自成)
2025-10-10 01:22:15 +02:00
committed by GitHub
parent 3769f50501
commit 9c67ff24e4
12 changed files with 157 additions and 111 deletions
+7 -7
View File
@@ -44,10 +44,10 @@ class Session {
return conn_->execute(_sql);
}
Result<Nothing> insert(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>& _data) {
return conn_->insert(_stmt, _data);
template <class ItBegin, class ItEnd>
Result<Nothing> insert(const dynamic::Insert& _stmt, ItBegin _begin,
ItEnd _end) {
return conn_->insert(_stmt, _begin, _end);
}
Session& operator=(const Session& _other) = delete;
@@ -79,9 +79,9 @@ class Session {
Result<Nothing> end_write() { return conn_->end_write(); }
Result<Nothing> write(
const std::vector<std::vector<std::optional<std::string>>>& _data) {
return conn_->write(_data);
template <class ItBegin, class ItEnd>
Result<Nothing> write(ItBegin _begin, ItEnd _end) {
return conn_->write(_begin, _end);
}
private:
+7 -7
View File
@@ -55,10 +55,10 @@ class Transaction {
return conn_->execute(_sql);
}
Result<Nothing> insert(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>& _data) {
return conn_->insert(_stmt, _data);
template <class ItBegin, class ItEnd>
Result<Nothing> insert(const dynamic::Insert& _stmt, ItBegin _begin,
ItEnd _end) {
return conn_->insert(_stmt, _begin, _end);
}
Transaction& operator=(const Transaction& _other) = delete;
@@ -98,9 +98,9 @@ class Transaction {
Result<Nothing> end_write() { return conn_->end_write(); }
Result<Nothing> write(
const std::vector<std::vector<std::optional<std::string>>>& _data) {
return conn_->write(_data);
template <class ItBegin, class ItEnd>
Result<Nothing> write(ItBegin _begin, ItEnd _end) {
return conn_->write(_begin, _end);
}
private:
+3 -21
View File
@@ -29,27 +29,9 @@ Result<Ref<Connection>> insert_impl(const Ref<Connection>& _conn,
const auto insert_stmt =
transpilation::to_insert_or_write<T, dynamic::Insert>(_or_replace);
std::vector<std::vector<std::optional<std::string>>> data;
for (auto it = _begin; it != _end; ++it) {
data.emplace_back(internal::to_str_vec(*it));
if (data.size() == SQLGEN_BATCH_SIZE) {
const auto res = _conn->insert(insert_stmt, data);
if (!res) {
return error(res.error().what());
}
data.clear();
}
}
if (data.size() != 0) {
const auto res = _conn->insert(insert_stmt, data);
if (!res) {
return error(res.error().what());
}
}
return _conn;
return _conn->insert(insert_stmt, _begin, _end).transform([&](const auto&) {
return _conn;
});
}
template <class ItBegin, class ItEnd, class Connection>
@@ -0,0 +1,35 @@
#ifndef SQLGEN_INTERNAL_WRITE_OR_INSERT_HPP_
#define SQLGEN_INTERNAL_WRITE_OR_INSERT_HPP_
#include <optional>
#include <string>
#include <vector>
#include "batch_size.hpp"
#include "to_str_vec.hpp"
namespace sqlgen::internal {
template <class FuncType, class ItBegin, class ItEnd>
Result<Nothing> write_or_insert(const FuncType& _actual_insert, ItBegin _begin,
ItEnd _end) noexcept {
std::vector<std::vector<std::optional<std::string>>> data;
for (auto it = _begin; it != _end; ++it) {
data.emplace_back(to_str_vec(*it));
if (data.size() == SQLGEN_BATCH_SIZE) {
const auto res = _actual_insert(data);
if (!res) {
return res;
}
data.clear();
}
}
if (data.size() != 0) {
return _actual_insert(data);
}
return Nothing{};
}
} // namespace sqlgen::internal
#endif
+30 -33
View File
@@ -18,48 +18,45 @@ namespace sqlgen {
/// The concept any connection needs to implements.
template <class ConnType>
concept is_connection =
requires(ConnType c, std::string _sql, dynamic::Statement _stmt,
dynamic::SelectFrom _select_from, dynamic::Insert _insert,
const dynamic::Write& _write,
std::vector<std::vector<std::optional<std::string>>> _data) {
/// Begins a transaction.
{ c.begin_transaction() } -> std::same_as<Result<Nothing>>;
concept is_connection = requires(
ConnType c, std::string _sql, dynamic::Statement _stmt,
dynamic::SelectFrom _select_from, dynamic::Insert _insert,
const dynamic::Write& _write, std::vector<internal::MockTable> _data) {
/// Begins a transaction.
{ c.begin_transaction() } -> std::same_as<Result<Nothing>>;
/// Commits a transaction.
{ c.commit() } -> std::same_as<Result<Nothing>>;
/// Commits a transaction.
{ c.commit() } -> std::same_as<Result<Nothing>>;
/// Executes a statement.
{ c.execute(_sql) } -> std::same_as<Result<Nothing>>;
/// Executes a statement.
{ c.execute(_sql) } -> std::same_as<Result<Nothing>>;
/// Inserts data into the database using the INSERT statement.
/// More minimal approach than write, but can be used inside transactions.
{ c.insert(_insert, _data) } -> std::same_as<Result<Nothing>>;
/// Inserts data into the database using the INSERT statement.
/// More minimal approach than write, but can be used inside transactions.
{
c.insert(_insert, _data.begin(), _data.end())
} -> std::same_as<Result<Nothing>>;
/// Reads the results of a SelectFrom statement.
{
c.template read<std::vector<internal::MockTable>>(_select_from)
} -> std::same_as<Result<std::vector<internal::MockTable>>>;
/// Reads the results of a SelectFrom statement.
{
c.template read<std::vector<internal::MockTable>>(_select_from)
} -> std::same_as<Result<std::vector<internal::MockTable>>>;
/// Commits a transaction.
{ c.rollback() } -> std::same_as<Result<Nothing>>;
/// Commits a transaction.
{ c.rollback() } -> std::same_as<Result<Nothing>>;
/// Transpiles a statement to a particular SQL dialect.
{ c.to_sql(_stmt) } -> std::same_as<std::string>;
/// Transpiles a statement to a particular SQL dialect.
{ c.to_sql(_stmt) } -> std::same_as<std::string>;
/// Starts the write operation.
{ c.start_write(_write) } -> std::same_as<Result<Nothing>>;
/// Starts the write operation.
{ c.start_write(_write) } -> std::same_as<Result<Nothing>>;
/// Ends the write operation and thus commits the results.
{ c.end_write() } -> std::same_as<Result<Nothing>>;
/// Ends the write operation and thus commits the results.
{ c.end_write() } -> std::same_as<Result<Nothing>>;
/// Writes data into a table. Each vector in data MUST have the same
/// length as _stmt.columns. You MUST call .start_write(...) first and
/// call .end_write() after all the data has been written. You CAN write
/// the data in chunks, meaning you can call .write(...) more than once
/// between .start_write(...) and .end_write().
{ c.write(_data) } -> std::same_as<Result<Nothing>>;
};
/// Writes data into a table.
{ c.write(_data.begin(), _data.end()) } -> std::same_as<Result<Nothing>>;
};
} // namespace sqlgen
+21 -6
View File
@@ -17,6 +17,7 @@
#include "../dynamic/Statement.hpp"
#include "../dynamic/Write.hpp"
#include "../internal/to_container.hpp"
#include "../internal/write_or_insert.hpp"
#include "../is_connection.hpp"
#include "../transpilation/value_t.hpp"
#include "Credentials.hpp"
@@ -48,10 +49,13 @@ class Connection {
return exec(conn_, _sql);
}
Result<Nothing> insert(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>&
_data) noexcept;
template <class ItBegin, class ItEnd>
Result<Nothing> insert(const dynamic::Insert& _stmt, ItBegin _begin,
ItEnd _end) noexcept {
return internal::write_or_insert(
[&](const auto& _data) { return insert_impl(_stmt, _data); }, _begin,
_end);
}
template <class ContainerType>
auto read(const dynamic::SelectFrom& _query) {
@@ -68,8 +72,11 @@ class Connection {
Result<Nothing> start_write(const dynamic::Write& _stmt);
Result<Nothing> write(
const std::vector<std::vector<std::optional<std::string>>>& _data);
template <class ItBegin, class ItEnd>
Result<Nothing> write(ItBegin _begin, ItEnd _end) {
return internal::write_or_insert(
[&](const auto& _data) { return write_impl(_data); }, _begin, _end);
}
Result<Nothing> end_write();
@@ -80,6 +87,11 @@ class Connection {
const std::vector<std::vector<std::optional<std::string>>>& _data,
MYSQL_STMT* _stmt) const noexcept;
Result<Nothing> insert_impl(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>&
_data) noexcept;
static ConnPtr make_conn(const Credentials& _credentials);
Result<StmtPtr> prepare_statement(
@@ -88,6 +100,9 @@ class Connection {
Result<Ref<IteratorBase>> read_impl(const dynamic::SelectFrom& _query);
Result<Nothing> write_impl(
const std::vector<std::vector<std::optional<std::string>>>& _data);
private:
/// A prepared statement - needed for the read and write operations. Note that
/// we have declared it before conn_, meaning it will be destroyed first.
+21 -6
View File
@@ -16,6 +16,7 @@
#include "../dynamic/Statement.hpp"
#include "../dynamic/Write.hpp"
#include "../internal/to_container.hpp"
#include "../internal/write_or_insert.hpp"
#include "../is_connection.hpp"
#include "../transpilation/value_t.hpp"
#include "Credentials.hpp"
@@ -44,10 +45,13 @@ class Connection {
return exec(conn_, _sql).transform([](auto&&) { return Nothing{}; });
}
Result<Nothing> insert(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>&
_data) noexcept;
template <class ItBegin, class ItEnd>
Result<Nothing> insert(const dynamic::Insert& _stmt, ItBegin _begin,
ItEnd _end) noexcept {
return internal::write_or_insert(
[&](const auto& _data) { return insert_impl(_stmt, _data); }, _begin,
_end);
}
template <class ContainerType>
auto read(const dynamic::SelectFrom& _query) {
@@ -68,10 +72,18 @@ class Connection {
Result<Nothing> end_write();
Result<Nothing> write(
const std::vector<std::vector<std::optional<std::string>>>& _data);
template <class ItBegin, class ItEnd>
Result<Nothing> write(ItBegin _begin, ItEnd _end) {
return internal::write_or_insert(
[&](const auto& _data) { return write_impl(_data); }, _begin, _end);
}
private:
Result<Nothing> insert_impl(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>&
_data) noexcept;
static ConnPtr make_conn(const std::string& _conn_str);
Result<Ref<IteratorBase>> read_impl(const dynamic::SelectFrom& _query);
@@ -79,6 +91,9 @@ class Connection {
std::string to_buffer(
const std::vector<std::optional<std::string>>& _line) const noexcept;
Result<Nothing> write_impl(
const std::vector<std::vector<std::optional<std::string>>>& _data);
private:
ConnPtr conn_;
+23 -6
View File
@@ -15,6 +15,7 @@
#include "../Transaction.hpp"
#include "../dynamic/Write.hpp"
#include "../internal/to_container.hpp"
#include "../internal/write_or_insert.hpp"
#include "../is_connection.hpp"
#include "../transpilation/value_t.hpp"
#include "to_sql.hpp"
@@ -39,10 +40,13 @@ class Connection {
Result<Nothing> execute(const std::string& _sql) noexcept;
Result<Nothing> insert(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>&
_data) noexcept;
template <class ItBegin, class ItEnd>
Result<Nothing> insert(const dynamic::Insert& _stmt, ItBegin _begin,
ItEnd _end) noexcept {
return internal::write_or_insert(
[&](const auto& _data) { return insert_impl(_stmt, _data); }, _begin,
_end);
}
template <class ContainerType>
auto read(const dynamic::SelectFrom& _query) {
@@ -61,8 +65,11 @@ class Connection {
Result<Nothing> end_write();
Result<Nothing> write(
const std::vector<std::vector<std::optional<std::string>>>& _data);
template <class ItBegin, class ItEnd>
Result<Nothing> write(ItBegin _begin, ItEnd _end) {
return internal::write_or_insert(
[&](const auto& _data) { return write_impl(_data); }, _begin, _end);
}
private:
/// Generates the underlying connection.
@@ -74,12 +81,22 @@ class Connection {
const std::vector<std::vector<std::optional<std::string>>>& _data,
sqlite3_stmt* _stmt) const noexcept;
/// Implements the actual insert.
Result<Nothing> insert_impl(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>&
_data) noexcept;
/// Generates a prepared statment, usually for inserts.
Result<StmtPtr> prepare_statement(const std::string& _sql) const noexcept;
/// Implements the actual read.
Result<Ref<IteratorBase>> read_impl(const dynamic::SelectFrom& _query);
/// Implements the actual write
Result<Nothing> write_impl(
const std::vector<std::vector<std::optional<std::string>>>& _data);
private:
/// A prepared statement - needed for the read and write operations. Note that
/// we have declared it before conn_, meaning it will be destroyed first.
+4 -19
View File
@@ -35,26 +35,11 @@ Result<Ref<Connection>> write(const Ref<Connection>& _conn, ItBegin _begin,
};
const auto write = [&](const auto&) -> Result<Nothing> {
std::vector<std::vector<std::optional<std::string>>> data;
for (auto it = _begin; it != _end; ++it) {
data.emplace_back(internal::to_str_vec(*it));
if (data.size() == SQLGEN_BATCH_SIZE) {
const auto res = _conn->write(data);
if (!res) {
_conn->end_write();
return res;
}
data.clear();
}
const auto res = _conn->write(_begin, _end);
if (!res) {
_conn->end_write();
}
if (data.size() != 0) {
const auto res = _conn->write(data);
if (!res) {
_conn->end_write();
return res;
}
}
return Nothing{};
return res;
};
const auto end_write = [&](const auto&) -> Result<Nothing> {
+2 -2
View File
@@ -70,7 +70,7 @@ Result<Nothing> Connection::actual_insert(
return Nothing{};
}
Result<Nothing> Connection::insert(
Result<Nothing> Connection::insert_impl(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>&
_data) noexcept {
@@ -157,7 +157,7 @@ Result<Nothing> Connection::start_write(const dynamic::Write& _write_stmt) {
});
}
Result<Nothing> Connection::write(
Result<Nothing> Connection::write_impl(
const std::vector<std::vector<std::optional<std::string>>>& _data) {
if (!stmt_) {
return error(
+2 -2
View File
@@ -28,7 +28,7 @@ Result<Nothing> Connection::end_write() {
return Nothing{};
}
Result<Nothing> Connection::insert(
Result<Nothing> Connection::insert_impl(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>&
_data) noexcept {
@@ -139,7 +139,7 @@ std::string Connection::to_buffer(
"\n";
}
Result<Nothing> Connection::write(
Result<Nothing> Connection::write_impl(
const std::vector<std::vector<std::optional<std::string>>>& _data) {
for (const auto& line : _data) {
const auto buffer = to_buffer(line);
+2 -2
View File
@@ -78,7 +78,7 @@ Result<Nothing> Connection::execute(const std::string& _sql) noexcept {
return Nothing{};
}
Result<Nothing> Connection::insert(
Result<Nothing> Connection::insert_impl(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>&
_data) noexcept {
@@ -158,7 +158,7 @@ Result<Nothing> Connection::start_write(const dynamic::Write& _stmt) {
.and_then([&](const auto&) { return begin_transaction(); });
}
Result<Nothing> Connection::write(
Result<Nothing> Connection::write_impl(
const std::vector<std::vector<std::optional<std::string>>>& _data) {
if (!stmt_) {
return error(