mirror of
https://github.com/getml/sqlgen.git
synced 2026-01-04 16:40:07 -06:00
Better transaction management (#10)
This commit is contained in:
committed by
GitHub
parent
87b911bba2
commit
622c44efbb
@@ -8,7 +8,7 @@ Transactions in sqlgen are managed through three main functions:
|
||||
|
||||
- `sqlgen::begin_transaction` - Starts a new transaction
|
||||
- `sqlgen::commit` - Commits the current transaction
|
||||
- `sqlgen::rollback` - Rolls back the current transaction
|
||||
- `sqlgen::rollback` - Rolls back the current transaction
|
||||
|
||||
Here's a basic example of how to use transactions:
|
||||
|
||||
@@ -27,7 +27,7 @@ auto conn = sqlite::connect("database.db")
|
||||
|
||||
## Automatic Rollback
|
||||
|
||||
sqlgen provides automatic rollback protection through RAII (Resource Acquisition Is Initialization). If a transaction is not explicitly committed, it will be automatically rolled back when the connection object goes out of scope. This helps ensure database consistency.
|
||||
sqlgen provides automatic rollback protection through RAII (Resource Acquisition Is Initialization). `begin_transaction` creates a `sqlgen::Transaction` object. If a transaction is not explicitly committed, it will be automatically rolled back when the transaction object goes out of scope. This helps ensure database consistency.
|
||||
|
||||
## Error Handling
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef SQLGEN_HPP_
|
||||
#define SQLGEN_HPP_
|
||||
|
||||
#include "sqlgen/Connection.hpp"
|
||||
#include "sqlgen/Flatten.hpp"
|
||||
#include "sqlgen/Iterator.hpp"
|
||||
#include "sqlgen/IteratorBase.hpp"
|
||||
@@ -24,6 +23,7 @@
|
||||
#include "sqlgen/if_exists.hpp"
|
||||
#include "sqlgen/if_not_exists.hpp"
|
||||
#include "sqlgen/insert.hpp"
|
||||
#include "sqlgen/is_connection.hpp"
|
||||
#include "sqlgen/limit.hpp"
|
||||
#include "sqlgen/order_by.hpp"
|
||||
#include "sqlgen/patterns.hpp"
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
#ifndef SQLGEN_CONNECTION_HPP_
|
||||
#define SQLGEN_CONNECTION_HPP_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "IteratorBase.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "dynamic/SelectFrom.hpp"
|
||||
#include "dynamic/Statement.hpp"
|
||||
#include "dynamic/Write.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
/// Abstract base class to be implemented by the different
|
||||
/// database connections.
|
||||
struct Connection {
|
||||
virtual ~Connection() = default;
|
||||
|
||||
/// Begins a transaction.
|
||||
virtual Result<Nothing> begin_transaction() = 0;
|
||||
|
||||
/// Commits a transaction.
|
||||
virtual Result<Nothing> commit() = 0;
|
||||
|
||||
/// Executes a statement. Note that in order for the statement to take effect,
|
||||
/// you must call .commit() afterwards.
|
||||
virtual Result<Nothing> execute(const std::string& _sql) = 0;
|
||||
|
||||
/// Inserts data into the database using the INSERT statement.
|
||||
/// More minimal approach than write, but can be used inside transactions.
|
||||
virtual Result<Nothing> insert(
|
||||
const dynamic::Insert& _stmt,
|
||||
const std::vector<std::vector<std::optional<std::string>>>& _data) = 0;
|
||||
|
||||
/// Reads the results of a SelectFrom statement.
|
||||
virtual Result<Ref<IteratorBase>> read(const dynamic::SelectFrom& _query) = 0;
|
||||
|
||||
/// Rolls a transaction back.
|
||||
virtual Result<Nothing> rollback() = 0;
|
||||
|
||||
/// Transpiles a statement to a particular SQL dialect.
|
||||
virtual std::string to_sql(const dynamic::Statement& _stmt) = 0;
|
||||
|
||||
/// Starts the write operation.
|
||||
virtual Result<Nothing> start_write(const dynamic::Write& _stmt) = 0;
|
||||
|
||||
/// Ends the write operation and thus commits the results.
|
||||
virtual Result<Nothing> end_write() = 0;
|
||||
|
||||
/// 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().
|
||||
virtual Result<Nothing> write(
|
||||
const std::vector<std::vector<std::optional<std::string>>>& _data) = 0;
|
||||
};
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
#endif
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
|
||||
#include "IteratorBase.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "internal/batch_size.hpp"
|
||||
|
||||
113
include/sqlgen/Transaction.hpp
Normal file
113
include/sqlgen/Transaction.hpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#ifndef SQLGEN_TRANSACTION_HPP_
|
||||
#define SQLGEN_TRANSACTION_HPP_
|
||||
|
||||
#include "Ref.hpp"
|
||||
#include "is_connection.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class _ConnType>
|
||||
requires is_connection<_ConnType>
|
||||
class Transaction {
|
||||
public:
|
||||
using ConnType = _ConnType;
|
||||
|
||||
Transaction(const Ref<ConnType>& _conn)
|
||||
: conn_(_conn), transaction_ended_(false) {}
|
||||
|
||||
Transaction(const Transaction& _other) = delete;
|
||||
|
||||
Transaction(Transaction&& _other) noexcept
|
||||
: conn_(std::move(_other.conn_)),
|
||||
transaction_ended_(_other.transaction_ended_) {
|
||||
_other.transaction_ended_ = true;
|
||||
}
|
||||
|
||||
~Transaction() {
|
||||
if (!transaction_ended_) {
|
||||
rollback();
|
||||
}
|
||||
}
|
||||
|
||||
Result<Nothing> begin_transaction() {
|
||||
if (!transaction_ended_) {
|
||||
return error("Transaction has already begun, cannot begin another.");
|
||||
}
|
||||
return conn_->begin_transaction().transform([&](const auto& _nothing) {
|
||||
transaction_ended_ = false;
|
||||
return _nothing;
|
||||
});
|
||||
}
|
||||
|
||||
Result<Nothing> commit() {
|
||||
if (transaction_ended_) {
|
||||
return error("Transaction has already ended, cannot commit.");
|
||||
}
|
||||
return conn_->commit().transform([&](const auto& _nothing) {
|
||||
transaction_ended_ = true;
|
||||
return _nothing;
|
||||
});
|
||||
}
|
||||
|
||||
const Ref<ConnType>& conn() const noexcept { return conn_; }
|
||||
|
||||
Result<Nothing> execute(const std::string& _sql) {
|
||||
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);
|
||||
}
|
||||
|
||||
Transaction& operator=(const Transaction& _other) = delete;
|
||||
|
||||
Transaction& operator=(Transaction&& _other) noexcept {
|
||||
if (this == &_other) {
|
||||
return *this;
|
||||
}
|
||||
conn_ = _other.conn;
|
||||
transaction_ended_ = _other.transaction_ended_;
|
||||
_other.transaction_ended_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Result<Ref<IteratorBase>> read(const dynamic::SelectFrom& _query) {
|
||||
return conn_->read(_query);
|
||||
}
|
||||
|
||||
Result<Nothing> rollback() noexcept {
|
||||
if (transaction_ended_) {
|
||||
return error("Transaction has already ended, cannot roll back.");
|
||||
}
|
||||
return conn_->rollback().transform([&](const auto& _nothing) {
|
||||
transaction_ended_ = true;
|
||||
return _nothing;
|
||||
});
|
||||
}
|
||||
|
||||
std::string to_sql(const dynamic::Statement& _stmt) noexcept {
|
||||
return conn_->to_sql(_stmt);
|
||||
}
|
||||
|
||||
Result<Nothing> start_write(const dynamic::Write& _stmt) {
|
||||
return conn_->to_sql(_stmt);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<ConnType> conn_;
|
||||
|
||||
bool transaction_ended_;
|
||||
};
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
#endif
|
||||
@@ -3,31 +3,32 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "Connection.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "Transaction.hpp"
|
||||
#include "is_connection.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
inline Result<Ref<Connection>> begin_transaction_impl(
|
||||
template <class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Transaction<Connection>>> begin_transaction_impl(
|
||||
const Ref<Connection>& _conn) {
|
||||
return _conn->begin_transaction().transform(
|
||||
[&](const auto&) { return _conn; });
|
||||
[&](const auto&) { return Ref<Transaction<Connection>>::make(_conn); });
|
||||
}
|
||||
|
||||
inline Result<Ref<Connection>> begin_transaction_impl(
|
||||
template <class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Transaction<Connection>>> begin_transaction_impl(
|
||||
const Result<Ref<Connection>>& _res) {
|
||||
return _res.and_then(
|
||||
[&](const auto& _conn) { return begin_transaction_impl(_conn); });
|
||||
}
|
||||
|
||||
struct BeginTransaction {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return begin_transaction_impl(_conn);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
auto operator()(const auto& _conn) const {
|
||||
return begin_transaction_impl(_conn);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,29 +3,27 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "Connection.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "Transaction.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
inline Result<Ref<Connection>> commit_impl(const Ref<Connection>& _conn) {
|
||||
return _conn->commit().transform([&](const auto&) { return _conn; });
|
||||
template <class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> commit_impl(const Ref<Transaction<Connection>>& _t) {
|
||||
return _t->commit().transform([&](const auto&) { return _t->conn(); });
|
||||
}
|
||||
|
||||
inline Result<Ref<Connection>> commit_impl(
|
||||
const Result<Ref<Connection>>& _res) {
|
||||
return _res.and_then([&](const auto& _conn) { return commit_impl(_conn); });
|
||||
template <class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> commit_impl(
|
||||
const Result<Transaction<Connection>>& _res) {
|
||||
return _res.and_then([&](const auto& _t) { return commit_impl(_t); });
|
||||
}
|
||||
|
||||
struct Commit {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return commit_impl(_conn);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
}
|
||||
auto operator()(const auto& _conn) const { return commit_impl(_conn); }
|
||||
};
|
||||
|
||||
inline const auto commit = Commit{};
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "is_connection.hpp"
|
||||
#include "transpilation/columns_t.hpp"
|
||||
#include "transpilation/to_create_index.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class ValueType, class ColumnsType, class WhereType>
|
||||
template <class ValueType, class ColumnsType, class WhereType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> create_index_impl(const Ref<Connection>& _conn,
|
||||
const std::string& _name,
|
||||
const bool _unique,
|
||||
@@ -24,7 +26,8 @@ Result<Ref<Connection>> create_index_impl(const Ref<Connection>& _conn,
|
||||
});
|
||||
}
|
||||
|
||||
template <class ValueType, class ColumnsType, class WhereType>
|
||||
template <class ValueType, class ColumnsType, class WhereType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> create_index_impl(const Result<Ref<Connection>>& _res,
|
||||
const std::string& _name,
|
||||
const bool _unique,
|
||||
@@ -39,15 +42,10 @@ Result<Ref<Connection>> create_index_impl(const Result<Ref<Connection>>& _res,
|
||||
template <rfl::internal::StringLiteral _name, class ValueType, class WhereType,
|
||||
class... ColTypes>
|
||||
struct CreateIndex {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return create_index_impl<ValueType,
|
||||
transpilation::columns_t<ValueType, ColTypes...>,
|
||||
WhereType>(_conn, _name.str(), unique_,
|
||||
if_not_exists_, where_);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
auto operator()(const auto& _conn) const {
|
||||
return create_index_impl<
|
||||
ValueType, transpilation::columns_t<ValueType, ColTypes...>, WhereType>(
|
||||
_conn, _name.str(), unique_, if_not_exists_, where_);
|
||||
}
|
||||
|
||||
bool unique_ = false;
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
|
||||
#include <rfl.hpp>
|
||||
|
||||
#include "is_connection.hpp"
|
||||
#include "transpilation/to_create_table.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class ValueType>
|
||||
template <class ValueType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> create_table_impl(const Ref<Connection>& _conn,
|
||||
const bool _if_not_exists) {
|
||||
const auto query = transpilation::to_create_table<ValueType>(_if_not_exists);
|
||||
@@ -16,7 +18,8 @@ Result<Ref<Connection>> create_table_impl(const Ref<Connection>& _conn,
|
||||
});
|
||||
}
|
||||
|
||||
template <class ValueType>
|
||||
template <class ValueType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> create_table_impl(const Result<Ref<Connection>>& _res,
|
||||
const bool _if_not_exists) {
|
||||
return _res.and_then([&](const auto& _conn) {
|
||||
@@ -26,12 +29,8 @@ Result<Ref<Connection>> create_table_impl(const Result<Ref<Connection>>& _res,
|
||||
|
||||
template <class ValueType>
|
||||
struct CreateTable {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return create_table_impl<ValueType>(_conn, if_not_exists_);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
auto operator()(const auto& _conn) const {
|
||||
return create_table_impl<ValueType>(_conn, if_not_exists_);
|
||||
}
|
||||
|
||||
bool if_not_exists_;
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "Connection.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "is_connection.hpp"
|
||||
#include "transpilation/to_delete_from.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class ValueType, class WhereType>
|
||||
template <class ValueType, class WhereType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> delete_from_impl(const Ref<Connection>& _conn,
|
||||
const WhereType& _where) {
|
||||
const auto query =
|
||||
@@ -20,7 +21,8 @@ Result<Ref<Connection>> delete_from_impl(const Ref<Connection>& _conn,
|
||||
});
|
||||
}
|
||||
|
||||
template <class ValueType, class WhereType>
|
||||
template <class ValueType, class WhereType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> delete_from_impl(const Result<Ref<Connection>>& _res,
|
||||
const WhereType& _where) {
|
||||
return _res.and_then([&](const auto& _conn) {
|
||||
@@ -30,12 +32,8 @@ Result<Ref<Connection>> delete_from_impl(const Result<Ref<Connection>>& _res,
|
||||
|
||||
template <class ValueType, class WhereType = Nothing>
|
||||
struct DeleteFrom {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return delete_from_impl<ValueType, WhereType>(_conn, where_);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
auto operator()(const auto& _conn) const {
|
||||
return delete_from_impl<ValueType, WhereType>(_conn, where_);
|
||||
}
|
||||
|
||||
WhereType where_;
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "Connection.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "is_connection.hpp"
|
||||
#include "transpilation/to_drop.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class ValueType>
|
||||
template <class ValueType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> drop_impl(const Ref<Connection>& _conn,
|
||||
const bool _if_exists) {
|
||||
const auto query = transpilation::to_drop<ValueType>(_if_exists);
|
||||
@@ -19,7 +20,8 @@ Result<Ref<Connection>> drop_impl(const Ref<Connection>& _conn,
|
||||
});
|
||||
}
|
||||
|
||||
template <class ValueType>
|
||||
template <class ValueType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> drop_impl(const Result<Ref<Connection>>& _res,
|
||||
const bool _if_exists) {
|
||||
return _res.and_then([&](const auto& _conn) {
|
||||
@@ -29,12 +31,8 @@ Result<Ref<Connection>> drop_impl(const Result<Ref<Connection>>& _res,
|
||||
|
||||
template <class ValueType>
|
||||
struct Drop {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return drop_impl<ValueType>(_conn, if_exists_);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
auto operator()(const auto& _conn) const {
|
||||
return drop_impl<ValueType>(_conn, if_exists_);
|
||||
}
|
||||
|
||||
bool if_exists_ = false;
|
||||
|
||||
@@ -5,27 +5,26 @@
|
||||
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "is_connection.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
inline Result<Ref<Connection>> exec(const Ref<Connection>& _conn,
|
||||
const std::string& _sql) {
|
||||
template <class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> exec(const Ref<Connection>& _conn,
|
||||
const std::string& _sql) {
|
||||
return _conn->execute(_sql).transform([&](const auto&) { return _conn; });
|
||||
}
|
||||
|
||||
inline Result<Ref<Connection>> exec(const Result<Ref<Connection>>& _res,
|
||||
const std::string& _sql) {
|
||||
template <class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> exec(const Result<Ref<Connection>>& _res,
|
||||
const std::string& _sql) {
|
||||
return _res.and_then([&](const auto& _conn) { return exec(_conn, _sql); });
|
||||
}
|
||||
|
||||
struct Exec {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return exec(_conn, sql_);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
}
|
||||
auto operator()(const auto& _conn) const { return exec(_conn, sql_); }
|
||||
|
||||
std::string sql_;
|
||||
};
|
||||
|
||||
@@ -11,11 +11,13 @@
|
||||
|
||||
#include "internal/batch_size.hpp"
|
||||
#include "internal/to_str_vec.hpp"
|
||||
#include "is_connection.hpp"
|
||||
#include "transpilation/to_insert_or_write.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class ItBegin, class ItEnd>
|
||||
template <class ItBegin, class ItEnd, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> insert(const Ref<Connection>& _conn, ItBegin _begin,
|
||||
ItEnd _end) {
|
||||
using T =
|
||||
@@ -47,7 +49,8 @@ Result<Ref<Connection>> insert(const Ref<Connection>& _conn, ItBegin _begin,
|
||||
return _conn;
|
||||
}
|
||||
|
||||
template <class ItBegin, class ItEnd>
|
||||
template <class ItBegin, class ItEnd, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> insert(const Result<Ref<Connection>>& _res,
|
||||
ItBegin _begin, ItEnd _end) {
|
||||
return _res.and_then(
|
||||
@@ -55,7 +58,7 @@ Result<Ref<Connection>> insert(const Result<Ref<Connection>>& _res,
|
||||
}
|
||||
|
||||
template <class ContainerType>
|
||||
Result<Ref<Connection>> insert(const auto& _conn, const ContainerType& _data) {
|
||||
auto insert(const auto& _conn, const ContainerType& _data) {
|
||||
if constexpr (std::ranges::input_range<std::remove_cvref_t<ContainerType>>) {
|
||||
return insert(_conn, _data.begin(), _data.end());
|
||||
} else {
|
||||
@@ -64,20 +67,14 @@ Result<Ref<Connection>> insert(const auto& _conn, const ContainerType& _data) {
|
||||
}
|
||||
|
||||
template <class ContainerType>
|
||||
Result<Ref<Connection>> insert(
|
||||
const auto& _conn, const std::reference_wrapper<ContainerType>& _data) {
|
||||
auto insert(const auto& _conn,
|
||||
const std::reference_wrapper<ContainerType>& _data) {
|
||||
return insert(_conn, _data.get());
|
||||
}
|
||||
|
||||
template <class ContainerType>
|
||||
struct Insert {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return insert(_conn, data_);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
}
|
||||
auto operator()(const auto& _conn) const { return insert(_conn, data_); }
|
||||
|
||||
ContainerType data_;
|
||||
};
|
||||
|
||||
63
include/sqlgen/is_connection.hpp
Normal file
63
include/sqlgen/is_connection.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef SQLGEN_ISCONNECTION_HPP_
|
||||
#define SQLGEN_ISCONNECTION_HPP_
|
||||
|
||||
#include <concepts>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "IteratorBase.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "dynamic/SelectFrom.hpp"
|
||||
#include "dynamic/Statement.hpp"
|
||||
#include "dynamic/Write.hpp"
|
||||
|
||||
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>>;
|
||||
|
||||
/// Commits a transaction.
|
||||
{ c.commit() } -> 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>>;
|
||||
|
||||
/// Reads the results of a SelectFrom statement.
|
||||
{ c.read(_select_from) } -> std::same_as<Result<Ref<IteratorBase>>>;
|
||||
|
||||
/// 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>;
|
||||
|
||||
/// 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>>;
|
||||
|
||||
/// 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>>;
|
||||
};
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
#endif
|
||||
@@ -8,70 +8,61 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "../Connection.hpp"
|
||||
#include "../IteratorBase.hpp"
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../Transaction.hpp"
|
||||
#include "../dynamic/Column.hpp"
|
||||
#include "../dynamic/Statement.hpp"
|
||||
#include "../dynamic/Write.hpp"
|
||||
#include "../is_connection.hpp"
|
||||
#include "Credentials.hpp"
|
||||
#include "exec.hpp"
|
||||
#include "to_sql.hpp"
|
||||
|
||||
namespace sqlgen::postgres {
|
||||
|
||||
class Connection : public sqlgen::Connection {
|
||||
class Connection {
|
||||
using ConnPtr = Ref<PGconn>;
|
||||
|
||||
public:
|
||||
Connection(const Credentials& _credentials)
|
||||
: conn_(make_conn(_credentials.to_str())),
|
||||
credentials_(_credentials),
|
||||
transaction_started_(false) {}
|
||||
: conn_(make_conn(_credentials.to_str())), credentials_(_credentials) {}
|
||||
|
||||
static rfl::Result<Ref<sqlgen::Connection>> make(
|
||||
static rfl::Result<Ref<Connection>> make(
|
||||
const Credentials& _credentials) noexcept;
|
||||
|
||||
Connection(const Connection& _other) = delete;
|
||||
~Connection() = default;
|
||||
|
||||
Connection(Connection&& _other) noexcept;
|
||||
Result<Nothing> begin_transaction() noexcept;
|
||||
|
||||
~Connection();
|
||||
Result<Nothing> commit() noexcept;
|
||||
|
||||
Result<Nothing> begin_transaction() noexcept final;
|
||||
|
||||
Result<Nothing> commit() noexcept final;
|
||||
|
||||
Result<Nothing> execute(const std::string& _sql) noexcept final {
|
||||
Result<Nothing> execute(const std::string& _sql) noexcept {
|
||||
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 final;
|
||||
_data) noexcept;
|
||||
|
||||
Connection& operator=(const Connection& _other) = delete;
|
||||
Result<Ref<IteratorBase>> read(const dynamic::SelectFrom& _query);
|
||||
|
||||
Connection& operator=(Connection&& _other) noexcept;
|
||||
Result<Nothing> rollback() noexcept;
|
||||
|
||||
Result<Ref<IteratorBase>> read(const dynamic::SelectFrom& _query) final;
|
||||
|
||||
Result<Nothing> rollback() noexcept final;
|
||||
|
||||
std::string to_sql(const dynamic::Statement& _stmt) noexcept final {
|
||||
std::string to_sql(const dynamic::Statement& _stmt) noexcept {
|
||||
return postgres::to_sql_impl(_stmt);
|
||||
}
|
||||
|
||||
Result<Nothing> start_write(const dynamic::Write& _stmt) final {
|
||||
Result<Nothing> start_write(const dynamic::Write& _stmt) {
|
||||
return execute(postgres::to_sql_impl(_stmt));
|
||||
}
|
||||
|
||||
Result<Nothing> end_write() final;
|
||||
Result<Nothing> end_write();
|
||||
|
||||
Result<Nothing> write(
|
||||
const std::vector<std::vector<std::optional<std::string>>>& _data) final;
|
||||
const std::vector<std::vector<std::optional<std::string>>>& _data);
|
||||
|
||||
private:
|
||||
static ConnPtr make_conn(const std::string& _conn_str);
|
||||
@@ -83,10 +74,13 @@ class Connection : public sqlgen::Connection {
|
||||
ConnPtr conn_;
|
||||
|
||||
Credentials credentials_;
|
||||
|
||||
bool transaction_started_;
|
||||
};
|
||||
|
||||
static_assert(is_connection<Connection>,
|
||||
"Must fulfill the is_connection concept.");
|
||||
static_assert(is_connection<Transaction<Connection>>,
|
||||
"Must fulfill the is_connection concept.");
|
||||
|
||||
} // namespace sqlgen::postgres
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,18 +4,19 @@
|
||||
#include <ranges>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Connection.hpp"
|
||||
#include "Range.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "internal/is_range.hpp"
|
||||
#include "is_connection.hpp"
|
||||
#include "transpilation/to_select_from.hpp"
|
||||
#include "transpilation/value_t.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class ContainerType, class WhereType, class OrderByType,
|
||||
class LimitType>
|
||||
class LimitType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<ContainerType> read_impl(const Ref<Connection>& _conn,
|
||||
const WhereType& _where,
|
||||
const LimitType& _limit) {
|
||||
@@ -47,7 +48,8 @@ Result<ContainerType> read_impl(const Ref<Connection>& _conn,
|
||||
}
|
||||
|
||||
template <class ContainerType, class WhereType, class OrderByType,
|
||||
class LimitType>
|
||||
class LimitType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<ContainerType> read_impl(const Result<Ref<Connection>>& _res,
|
||||
const WhereType& _where,
|
||||
const LimitType& _limit) {
|
||||
@@ -60,30 +62,25 @@ Result<ContainerType> read_impl(const Result<Ref<Connection>>& _res,
|
||||
template <class Type, class WhereType = Nothing, class OrderByType = Nothing,
|
||||
class LimitType = Nothing>
|
||||
struct Read {
|
||||
Result<Type> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
if constexpr (std::ranges::input_range<std::remove_cvref_t<Type>>) {
|
||||
return read_impl<Type, WhereType, OrderByType, LimitType>(_conn, where_,
|
||||
limit_);
|
||||
Result<Type> operator()(const auto& _conn) const {
|
||||
if constexpr (std::ranges::input_range<std::remove_cvref_t<Type>>) {
|
||||
return read_impl<Type, WhereType, OrderByType, LimitType>(_conn, where_,
|
||||
limit_);
|
||||
|
||||
} else {
|
||||
const auto extract_result = [](auto&& _vec) -> Result<Type> {
|
||||
if (_vec.size() != 1) {
|
||||
return error(
|
||||
"Because the provided type was not a container, the query "
|
||||
"needs to return exactly one result, but it did return " +
|
||||
std::to_string(_vec.size()) + " results.");
|
||||
}
|
||||
return std::move(_vec[0]);
|
||||
};
|
||||
} else {
|
||||
const auto extract_result = [](auto&& _vec) -> Result<Type> {
|
||||
if (_vec.size() != 1) {
|
||||
return error(
|
||||
"Because the provided type was not a container, the query "
|
||||
"needs to return exactly one result, but it did return " +
|
||||
std::to_string(_vec.size()) + " results.");
|
||||
}
|
||||
return std::move(_vec[0]);
|
||||
};
|
||||
|
||||
return read_impl<std::vector<Type>, WhereType, OrderByType, LimitType>(
|
||||
_conn, where_, limit_)
|
||||
.and_then(extract_result);
|
||||
}
|
||||
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
return read_impl<std::vector<Type>, WhereType, OrderByType, LimitType>(
|
||||
_conn, where_, limit_)
|
||||
.and_then(extract_result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,23 +3,27 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "Connection.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "is_connection.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
inline Result<Ref<Connection>> rollback_impl(const Ref<Connection>& _conn) {
|
||||
return _conn->rollback().transform([&](const auto&) { return _conn; });
|
||||
template <class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> rollback_impl(const Ref<Transaction<Connection>>& _t) {
|
||||
return _t->rollback().transform([&](const auto&) { return _t->conn(); });
|
||||
}
|
||||
|
||||
inline Result<Ref<Connection>> rollback_impl(
|
||||
const Result<Ref<Connection>>& _res) {
|
||||
template <class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> rollback_impl(
|
||||
const Result<Ref<Transaction<Connection>>>& _res) {
|
||||
return _res.and_then([&](const auto& _conn) { return rollback_impl(_conn); });
|
||||
}
|
||||
|
||||
struct Rollback {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
auto operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return rollback_impl(_conn);
|
||||
} catch (std::exception& e) {
|
||||
|
||||
@@ -9,61 +9,53 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "../Connection.hpp"
|
||||
#include "../IteratorBase.hpp"
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../Transaction.hpp"
|
||||
#include "../dynamic/Write.hpp"
|
||||
#include "../is_connection.hpp"
|
||||
#include "to_sql.hpp"
|
||||
|
||||
namespace sqlgen::sqlite {
|
||||
|
||||
class Connection : public sqlgen::Connection {
|
||||
class Connection {
|
||||
using ConnPtr = Ref<sqlite3>;
|
||||
using StmtPtr = std::shared_ptr<sqlite3_stmt>;
|
||||
|
||||
public:
|
||||
Connection(const std::string& _fname)
|
||||
: stmt_(nullptr), conn_(make_conn(_fname)), transaction_started_(false) {}
|
||||
: stmt_(nullptr), conn_(make_conn(_fname)) {}
|
||||
|
||||
static rfl::Result<Ref<sqlgen::Connection>> make(
|
||||
const std::string& _fname) noexcept;
|
||||
static rfl::Result<Ref<Connection>> make(const std::string& _fname) noexcept;
|
||||
|
||||
Connection(const Connection& _other) = delete;
|
||||
~Connection() = default;
|
||||
|
||||
Connection(Connection&& _other) noexcept;
|
||||
Result<Nothing> begin_transaction() noexcept;
|
||||
|
||||
~Connection();
|
||||
Result<Nothing> commit() noexcept;
|
||||
|
||||
Result<Nothing> begin_transaction() noexcept final;
|
||||
|
||||
Result<Nothing> commit() noexcept final;
|
||||
|
||||
Result<Nothing> execute(const std::string& _sql) noexcept final;
|
||||
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 final;
|
||||
_data) noexcept;
|
||||
|
||||
Connection& operator=(const Connection& _other) = delete;
|
||||
Result<Ref<IteratorBase>> read(const dynamic::SelectFrom& _query);
|
||||
|
||||
Connection& operator=(Connection&& _other) noexcept;
|
||||
Result<Nothing> rollback() noexcept;
|
||||
|
||||
Result<Ref<IteratorBase>> read(const dynamic::SelectFrom& _query) final;
|
||||
|
||||
Result<Nothing> rollback() noexcept final;
|
||||
|
||||
std::string to_sql(const dynamic::Statement& _stmt) noexcept final {
|
||||
std::string to_sql(const dynamic::Statement& _stmt) noexcept {
|
||||
return sqlite::to_sql_impl(_stmt);
|
||||
}
|
||||
|
||||
Result<Nothing> start_write(const dynamic::Write& _stmt) final;
|
||||
Result<Nothing> start_write(const dynamic::Write& _stmt);
|
||||
|
||||
Result<Nothing> end_write() final;
|
||||
Result<Nothing> end_write();
|
||||
|
||||
Result<Nothing> write(
|
||||
const std::vector<std::vector<std::optional<std::string>>>& _data) final;
|
||||
const std::vector<std::vector<std::optional<std::string>>>& _data);
|
||||
|
||||
private:
|
||||
/// Generates the underlying connection.
|
||||
@@ -85,11 +77,13 @@ class Connection : public sqlgen::Connection {
|
||||
|
||||
/// The underlying sqlite3 connection.
|
||||
ConnPtr conn_;
|
||||
|
||||
/// Whether a transaction has been started.
|
||||
bool transaction_started_;
|
||||
};
|
||||
|
||||
static_assert(is_connection<Connection>,
|
||||
"Must fulfill the is_connection concept.");
|
||||
static_assert(is_connection<Transaction<Connection>>,
|
||||
"Must fulfill the is_connection concept.");
|
||||
|
||||
} // namespace sqlgen::sqlite
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Connection.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "is_connection.hpp"
|
||||
#include "transpilation/to_update.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class ValueType, class SetsType, class WhereType>
|
||||
template <class ValueType, class SetsType, class WhereType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> update_impl(const Ref<Connection>& _conn,
|
||||
const SetsType& _sets,
|
||||
const WhereType& _where) {
|
||||
@@ -22,7 +23,8 @@ Result<Ref<Connection>> update_impl(const Ref<Connection>& _conn,
|
||||
});
|
||||
}
|
||||
|
||||
template <class ValueType, class SetsType, class WhereType>
|
||||
template <class ValueType, class SetsType, class WhereType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> update_impl(const Result<Ref<Connection>>& _res,
|
||||
const SetsType& _sets,
|
||||
const WhereType& _where) {
|
||||
@@ -33,12 +35,8 @@ Result<Ref<Connection>> update_impl(const Result<Ref<Connection>>& _res,
|
||||
|
||||
template <class ValueType, class SetsType, class WhereType = Nothing>
|
||||
struct Update {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return update_impl<ValueType, SetsType, WhereType>(_conn, sets_, where_);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
auto operator()(const auto& _conn) const {
|
||||
return update_impl<ValueType, SetsType, WhereType>(_conn, sets_, where_);
|
||||
}
|
||||
|
||||
SetsType sets_;
|
||||
@@ -47,7 +45,7 @@ struct Update {
|
||||
};
|
||||
|
||||
template <class ValueType, class... SetsType>
|
||||
inline auto update(const SetsType&... _sets) {
|
||||
auto update(const SetsType&... _sets) {
|
||||
static_assert(sizeof...(_sets) > 0, "You must update at least one column.");
|
||||
using TupleType = rfl::Tuple<std::remove_cvref_t<SetsType>...>;
|
||||
return Update<ValueType, TupleType>{.sets_ = TupleType(_sets...)};
|
||||
|
||||
@@ -10,18 +10,19 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "Connection.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "dynamic/Write.hpp"
|
||||
#include "internal/batch_size.hpp"
|
||||
#include "internal/to_str_vec.hpp"
|
||||
#include "is_connection.hpp"
|
||||
#include "transpilation/to_create_table.hpp"
|
||||
#include "transpilation/to_insert_or_write.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class ItBegin, class ItEnd>
|
||||
template <class ItBegin, class ItEnd, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> write(const Ref<Connection>& _conn, ItBegin _begin,
|
||||
ItEnd _end) noexcept {
|
||||
using T =
|
||||
@@ -69,16 +70,16 @@ Result<Ref<Connection>> write(const Ref<Connection>& _conn, ItBegin _begin,
|
||||
.transform([&](const auto&) { return _conn; });
|
||||
}
|
||||
|
||||
template <class ItBegin, class ItEnd>
|
||||
template <class ItBegin, class ItEnd, class Connection>
|
||||
requires is_connection<Connection>
|
||||
Result<Ref<Connection>> write(const Result<Ref<Connection>>& _res,
|
||||
ItBegin _begin, ItEnd _end) noexcept {
|
||||
return _res.and_then(
|
||||
[&](const auto& _conn) { return write(_conn, _begin, _end); });
|
||||
}
|
||||
|
||||
template <class ConnectionType, class ContainerType>
|
||||
Result<Ref<Connection>> write(const ConnectionType& _conn,
|
||||
const ContainerType& _container) noexcept {
|
||||
template <class ContainerType>
|
||||
auto write(const auto& _conn, const ContainerType& _container) noexcept {
|
||||
if constexpr (std::ranges::input_range<std::remove_cvref_t<ContainerType>>) {
|
||||
return write(_conn, _container.begin(), _container.end());
|
||||
} else {
|
||||
@@ -86,22 +87,15 @@ Result<Ref<Connection>> write(const ConnectionType& _conn,
|
||||
}
|
||||
}
|
||||
|
||||
template <class ConnectionType, class ContainerType>
|
||||
Result<Ref<Connection>> write(
|
||||
const ConnectionType& _conn,
|
||||
const std::reference_wrapper<ContainerType>& _data) {
|
||||
template <class ContainerType>
|
||||
auto write(const auto& _conn,
|
||||
const std::reference_wrapper<ContainerType>& _data) {
|
||||
return write(_conn, _data.get());
|
||||
}
|
||||
|
||||
template <class ContainerType>
|
||||
struct Write {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return write(_conn, data_);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
}
|
||||
auto operator()(const auto& _conn) const { return write(_conn, data_); }
|
||||
|
||||
ContainerType data_;
|
||||
};
|
||||
|
||||
@@ -11,35 +11,11 @@
|
||||
|
||||
namespace sqlgen::postgres {
|
||||
|
||||
Connection::Connection(Connection&& _other) noexcept
|
||||
: conn_(std::move(_other.conn_)),
|
||||
credentials_(std::move(_other.credentials_)),
|
||||
transaction_started_(_other.transaction_started_) {
|
||||
_other.transaction_started_ = false;
|
||||
}
|
||||
|
||||
Connection::~Connection() {
|
||||
if (transaction_started_) {
|
||||
rollback();
|
||||
}
|
||||
}
|
||||
|
||||
Result<Nothing> Connection::begin_transaction() noexcept {
|
||||
if (transaction_started_) {
|
||||
return error(
|
||||
"Cannot BEGIN TRANSACTION - another transaction has been started.");
|
||||
}
|
||||
transaction_started_ = true;
|
||||
return execute("BEGIN TRANSACTION;");
|
||||
}
|
||||
|
||||
Result<Nothing> Connection::commit() noexcept {
|
||||
if (!transaction_started_) {
|
||||
return error("Cannot COMMIT - no transaction has been started.");
|
||||
}
|
||||
transaction_started_ = false;
|
||||
return execute("COMMIT;");
|
||||
}
|
||||
Result<Nothing> Connection::commit() noexcept { return execute("COMMIT;"); }
|
||||
|
||||
Result<Nothing> Connection::end_write() {
|
||||
if (PQputCopyEnd(conn_.get(), NULL) == -1) {
|
||||
@@ -108,10 +84,10 @@ Result<Nothing> Connection::insert(
|
||||
return execute("DEALLOCATE sqlgen_insert_into_table;");
|
||||
}
|
||||
|
||||
rfl::Result<Ref<sqlgen::Connection>> Connection::make(
|
||||
rfl::Result<Ref<Connection>> Connection::make(
|
||||
const Credentials& _credentials) noexcept {
|
||||
try {
|
||||
return Ref<sqlgen::Connection>(Ref<Connection>::make(_credentials));
|
||||
return Ref<Connection>::make(_credentials);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
@@ -131,20 +107,6 @@ typename Connection::ConnPtr Connection::make_conn(
|
||||
return ConnPtr::make(std::shared_ptr<PGconn>(raw_ptr, &PQfinish)).value();
|
||||
}
|
||||
|
||||
Connection& Connection::operator=(Connection&& _other) noexcept {
|
||||
if (this == &_other) {
|
||||
return *this;
|
||||
}
|
||||
if (transaction_started_) {
|
||||
rollback();
|
||||
}
|
||||
conn_ = std::move(_other.conn_);
|
||||
credentials_ = std::move(_other.credentials_);
|
||||
transaction_started_ = _other.transaction_started_;
|
||||
_other.transaction_started_ = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Result<Ref<IteratorBase>> Connection::read(const dynamic::SelectFrom& _query) {
|
||||
const auto sql = postgres::to_sql_impl(_query);
|
||||
try {
|
||||
@@ -154,13 +116,7 @@ Result<Ref<IteratorBase>> Connection::read(const dynamic::SelectFrom& _query) {
|
||||
}
|
||||
}
|
||||
|
||||
Result<Nothing> Connection::rollback() noexcept {
|
||||
if (!transaction_started_) {
|
||||
return error("Cannot ROLLBACK - no transaction has been started.");
|
||||
}
|
||||
transaction_started_ = false;
|
||||
return execute("ROLLBACK;");
|
||||
}
|
||||
Result<Nothing> Connection::rollback() noexcept { return execute("ROLLBACK;"); }
|
||||
|
||||
std::string Connection::to_buffer(
|
||||
const std::vector<std::optional<std::string>>& _line) const noexcept {
|
||||
|
||||
@@ -11,19 +11,6 @@
|
||||
|
||||
namespace sqlgen::sqlite {
|
||||
|
||||
Connection::Connection(Connection&& _other) noexcept
|
||||
: stmt_(std::move(_other.stmt_)),
|
||||
conn_(std::move(_other.conn_)),
|
||||
transaction_started_(_other.transaction_started_) {
|
||||
_other.transaction_started_ = false;
|
||||
}
|
||||
|
||||
Connection::~Connection() {
|
||||
if (transaction_started_) {
|
||||
rollback();
|
||||
}
|
||||
}
|
||||
|
||||
Result<Nothing> Connection::actual_insert(
|
||||
const std::vector<std::vector<std::optional<std::string>>>& _data,
|
||||
sqlite3_stmt* _stmt) const noexcept {
|
||||
@@ -66,26 +53,15 @@ Result<Nothing> Connection::actual_insert(
|
||||
}
|
||||
|
||||
Result<Nothing> Connection::begin_transaction() noexcept {
|
||||
if (transaction_started_) {
|
||||
return error(
|
||||
"Cannot BEGIN TRANSACTION - another transaction has been started.");
|
||||
}
|
||||
transaction_started_ = true;
|
||||
return execute("BEGIN TRANSACTION;");
|
||||
}
|
||||
|
||||
Result<Nothing> Connection::commit() noexcept {
|
||||
if (!transaction_started_) {
|
||||
return error("Cannot COMMIT - no transaction has been started.");
|
||||
}
|
||||
transaction_started_ = false;
|
||||
return execute("COMMIT;");
|
||||
}
|
||||
Result<Nothing> Connection::commit() noexcept { return execute("COMMIT;"); }
|
||||
|
||||
rfl::Result<Ref<sqlgen::Connection>> Connection::make(
|
||||
rfl::Result<Ref<Connection>> Connection::make(
|
||||
const std::string& _fname) noexcept {
|
||||
try {
|
||||
return Ref<sqlgen::Connection>(Ref<Connection>::make(_fname));
|
||||
return Ref<Connection>::make(_fname);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
@@ -121,20 +97,6 @@ typename Connection::ConnPtr Connection::make_conn(const std::string& _fname) {
|
||||
return ConnPtr::make(std::shared_ptr<sqlite3>(conn, &sqlite3_close)).value();
|
||||
}
|
||||
|
||||
Connection& Connection::operator=(Connection&& _other) noexcept {
|
||||
if (this == &_other) {
|
||||
return *this;
|
||||
}
|
||||
if (transaction_started_) {
|
||||
rollback();
|
||||
}
|
||||
stmt_ = std::move(_other.stmt_);
|
||||
conn_ = std::move(_other.conn_);
|
||||
transaction_started_ = _other.transaction_started_;
|
||||
_other.transaction_started_ = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Result<Ref<IteratorBase>> Connection::read(const dynamic::SelectFrom& _query) {
|
||||
const auto sql = to_sql_impl(_query);
|
||||
|
||||
@@ -176,13 +138,7 @@ Result<Connection::StmtPtr> Connection::prepare_statement(
|
||||
return StmtPtr(p_stmt, &sqlite3_finalize);
|
||||
}
|
||||
|
||||
Result<Nothing> Connection::rollback() noexcept {
|
||||
if (!transaction_started_) {
|
||||
return error("Cannot ROLLBACK - no transaction has been started.");
|
||||
}
|
||||
transaction_started_ = false;
|
||||
return execute("ROLLBACK;");
|
||||
}
|
||||
Result<Nothing> Connection::rollback() noexcept { return execute("ROLLBACK;"); }
|
||||
|
||||
Result<Nothing> Connection::start_write(const dynamic::Write& _stmt) {
|
||||
if (stmt_) {
|
||||
|
||||
Reference in New Issue
Block a user