3#include "../query/core.hpp"
4#include "../results/result.hpp"
16#include <boost/pfr.hpp>
41 std::string
host =
"localhost";
58 std::ostringstream conn_str;
61 conn_str <<
"host=" <<
host <<
" ";
63 conn_str <<
"port=" <<
port <<
" ";
65 conn_str <<
"dbname=" <<
dbname <<
" ";
68 conn_str <<
"user=" <<
user <<
" ";
71 conn_str <<
"password=" <<
password <<
" ";
80 conn_str <<
"sslmode=" <<
ssl_mode <<
" ";
83 conn_str <<
"sslcert=" <<
ssl_cert <<
" ";
86 conn_str <<
"sslkey=" <<
ssl_key <<
" ";
92 std::string result = conn_str.str();
93 if (!result.empty() && result.back() ==
' ') {
121 const std::string& sql,
const std::vector<std::string>& params = {}) = 0;
126 template <query::SqlExpr Query>
129 std::string sql = query.to_sql();
130 std::vector<std::string> params = query.bind_params();
141 template <
typename T, query::SqlExpr Query>
146 return std::unexpected(result.error());
149 const auto& result_set = *result;
150 if (result_set.empty()) {
158 const auto& row = result_set.at(0);
161 auto structure_tie = boost::pfr::structure_tie(obj);
165 if (result_set.column_count() != boost::pfr::tuple_size_v<std::remove_cvref_t<T>>) {
166 std::stringstream ss;
167 for (
const auto& param : query.bind_params()) {
171 .
message =
"Column count does not match struct field count, " +
172 std::to_string(result_set.column_count()) +
173 " != " + std::to_string(boost::pfr::tuple_size_v<std::remove_cvref_t<T>>) +
174 " for struct " +
typeid(T).name() +
" and query " + query.to_sql() +
175 " with params " + ss.str(),
182 std::vector<std::string> values;
183 for (
size_t i = 0; i < result_set.column_count(); ++i) {
184 auto cell_result = row.get_cell(i);
186 return std::unexpected(
190 values.push_back((*cell_result)->raw_value());
195 }
catch (
const std::exception& e) {
196 return std::unexpected(
209 template <
typename T, query::SqlExpr Query>
214 return std::unexpected(result.error());
217 const auto& result_set = *result;
218 std::vector<T> objects;
219 objects.reserve(result_set.size());
222 if (result_set.empty()) {
228 if (result_set.column_count() != boost::pfr::tuple_size_v<std::remove_cvref_t<T>>) {
229 std::stringstream ss;
230 for (
const auto& param : query.bind_params()) {
234 .
message =
"Column count does not match struct field count, " +
235 std::to_string(result_set.column_count()) +
236 " != " + std::to_string(boost::pfr::tuple_size_v<std::remove_cvref_t<T>>) +
237 " for struct " +
typeid(T).name() +
" and query " + query.to_sql() +
238 " with params " + ss.str(),
243 for (
size_t row_idx = 0; row_idx < result_set.size(); ++row_idx) {
244 const auto& row = result_set.at(row_idx);
246 auto structure_tie = boost::pfr::structure_tie(obj);
250 std::vector<std::string> values;
251 for (
size_t i = 0; i < result_set.column_count(); ++i) {
252 auto cell_result = row.get_cell(i);
255 cell_result.error().message,
258 values.push_back((*cell_result)->raw_value());
262 objects.push_back(std::move(obj));
263 }
catch (
const std::exception& e) {
265 .
message = std::string(
"Failed to convert result to struct: ") + e.what(),
Abstract base class for database connections.
virtual ConnectionResult< void > begin_transaction(IsolationLevel isolation_level=IsolationLevel::ReadCommitted)=0
Begin a new transaction.
virtual ConnectionResult< void > connect()=0
Connect to the database.
virtual bool is_connected() const =0
Check if the connection is open.
virtual bool in_transaction() const =0
Check if a transaction is currently active.
virtual ConnectionResult< void > commit_transaction()=0
Commit the current transaction.
ConnectionResult< T > execute(const Query &query)
Execute a query and map results to a user-defined type using Boost.PFR.
ConnectionResult< result::ResultSet > execute(const Query &query)
Execute a query expression.
virtual ConnectionResult< result::ResultSet > execute_raw(const std::string &sql, const std::vector< std::string > ¶ms={})=0
Execute a raw SQL query with parameters.
virtual ConnectionResult< void > disconnect()=0
Disconnect from the database.
ConnectionResult< std::vector< T > > execute_many(const Query &query)
Execute a query and map results to a vector of user-defined types.
virtual ~Connection()=default
Virtual destructor.
virtual ConnectionResult< void > rollback_transaction()=0
Rollback the current transaction.
void map_row_to_tuple(Tuple &tuple, const std::vector< std::string > &row)
Helper function to map a result row to a tuple (and thus to a struct)
IsolationLevel
Transaction isolation levels.
@ ReadUncommitted
Allows dirty reads.
@ RepeatableRead
Prevents non-repeatable reads.
@ Serializable
Highest isolation level, prevents phantom reads.
@ ReadCommitted
Prevents dirty reads.
std::expected< T, ConnectionError > ConnectionResult
Type alias for result of connection operations.
Error type for database connection operations.
Basic parameters for a PostgreSQL connection.
std::string ssl_root_cert
std::string to_connection_string() const
Convert parameters to a PostgreSQL connection string.
std::string application_name