From c8ce5f682f321ffc66a09f7081209cdfb6d020ad Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sat, 19 Apr 2025 10:12:12 +0200 Subject: [PATCH] Started developing postgres --- include/sqlgen/postgres.hpp | 8 ++++ include/sqlgen/postgres/Connection.hpp | 55 +++++++++++++++++++++++ include/sqlgen/postgres/Credentials.hpp | 23 ++++++++++ include/sqlgen/postgres/Iterator.hpp | 37 ++++++++++++++++ include/sqlgen/postgres/connect.hpp | 17 +++++++ src/sqlgen/postgres/Connection.cpp | 36 +++++++++++++++ src/sqlgen/postgres/Iterator.cpp | 59 +++++++++++++++++++++++++ 7 files changed, 235 insertions(+) create mode 100644 include/sqlgen/postgres.hpp create mode 100644 include/sqlgen/postgres/Connection.hpp create mode 100644 include/sqlgen/postgres/Credentials.hpp create mode 100644 include/sqlgen/postgres/Iterator.hpp create mode 100644 include/sqlgen/postgres/connect.hpp create mode 100644 src/sqlgen/postgres/Connection.cpp create mode 100644 src/sqlgen/postgres/Iterator.cpp diff --git a/include/sqlgen/postgres.hpp b/include/sqlgen/postgres.hpp new file mode 100644 index 0000000..5ee513b --- /dev/null +++ b/include/sqlgen/postgres.hpp @@ -0,0 +1,8 @@ +#ifndef SQLGEN_POSTGRES_HPP_ +#define SQLGEN_POSTGRES_HPP_ + +#include "../sqlgen.hpp" +#include "postgres/Credentials.hpp" +#include "postgres/connect.hpp" + +#endif diff --git a/include/sqlgen/postgres/Connection.hpp b/include/sqlgen/postgres/Connection.hpp new file mode 100644 index 0000000..25bebbd --- /dev/null +++ b/include/sqlgen/postgres/Connection.hpp @@ -0,0 +1,55 @@ +#ifndef SQLGEN_POSTGRES_CONNECTION_HPP_ +#define SQLGEN_POSTGRES_CONNECTION_HPP_ + +#include + +#include +#include +#include +#include + +#include "../Connection.hpp" +#include "../IteratorBase.hpp" +#include "../Ref.hpp" +#include "../Result.hpp" +#include "../dynamic/Column.hpp" +#include "../dynamic/Statement.hpp" +#include "Credentials.hpp" + +namespace sqlgen::postgres { + +class Connection : public sqlgen::Connection { + using ConnPtr = Ref; + + public: + Connection(const Credentials& _credentials) + : credentials_(_credentials), conn_(make_conn(_credentials.to_str())) {} + + static rfl::Result> make( + const Credentials& _credentials) noexcept; + + ~Connection() = default; + + Result commit() final { return execute("COMMIT;"); } + + Result execute(const std::string& _sql) noexcept final; + + Result> read(const dynamic::SelectFrom& _query) final; + + std::string to_sql(const dynamic::Statement& _stmt) noexcept final; + + Result start_write(const dynamic::Insert& _stmt) final; + + Result end_write() final; + + Result write( + const std::vector>>& _data) final; + + private: + static rfl::Result> make_conn( + const std::string& _conn_str); +}; + +} // namespace sqlgen::postgres + +#endif diff --git a/include/sqlgen/postgres/Credentials.hpp b/include/sqlgen/postgres/Credentials.hpp new file mode 100644 index 0000000..318041c --- /dev/null +++ b/include/sqlgen/postgres/Credentials.hpp @@ -0,0 +1,23 @@ +#ifndef SQLGEN_POSTGRES_CREDENTIALS_HPP_ +#define SQLGEN_POSTGRES_CREDENTIALS_HPP_ + +#include + +namespace sqlgen::postgres { + +struct Credentials { + std::string user; + std::string password; + std::string host; + std::string dbname; + int port = 5432; + + std::string to_str() const { + return "postgresql://" + user + ":" + password + "@" + host + ":" + + std::to_string(port) + "/" + dbname; + } +}; + +} // namespace sqlgen::postgres + +#endif diff --git a/include/sqlgen/postgres/Iterator.hpp b/include/sqlgen/postgres/Iterator.hpp new file mode 100644 index 0000000..29debd9 --- /dev/null +++ b/include/sqlgen/postgres/Iterator.hpp @@ -0,0 +1,37 @@ +#ifndef SQLGEN_POSTGRES_ITERATOR_HPP_ +#define SQLGEN_POSTGRES_ITERATOR_HPP_ + +#include + +#include +#include +#include + +#include "../IteratorBase.hpp" +#include "../Ref.hpp" +#include "../Result.hpp" +#include "Connection.hpp" + +namespace sqlgen::postgres { + +class Iterator : public sqlgen::IteratorBase { + public: + Iterator(const StmtPtr& _stmt, const ConnPtr& _conn); + + ~Iterator(); + + /// Whether the end of the available data has been reached. + bool end() const final; + + /// Returns the next batch of rows. + /// If _batch_size is greater than the number of rows left, returns all + /// of the rows left. + Result>>> next( + const size_t _batch_size) final; + + private: +}; + +} // namespace sqlgen::postgres + +#endif diff --git a/include/sqlgen/postgres/connect.hpp b/include/sqlgen/postgres/connect.hpp new file mode 100644 index 0000000..a6573d4 --- /dev/null +++ b/include/sqlgen/postgres/connect.hpp @@ -0,0 +1,17 @@ +#ifndef SQLGEN_POSTGRES_CONNECT_HPP_ +#define SQLGEN_POSTGRES_CONNECT_HPP_ + +#include + +#include "Connection.hpp" +#include "Credentials.hpp" + +namespace sqlgen::postgres { + +inline auto connect(const Credentials& _credentials) { + return Connection::make(_credentials); +} + +} // namespace sqlgen::postgres + +#endif diff --git a/src/sqlgen/postgres/Connection.cpp b/src/sqlgen/postgres/Connection.cpp new file mode 100644 index 0000000..acdbf53 --- /dev/null +++ b/src/sqlgen/postgres/Connection.cpp @@ -0,0 +1,36 @@ +#include "sqlgen/postgres/Connection.hpp" + +#include +#include +#include +#include + +#include "sqlgen/internal/collect/vector.hpp" +#include "sqlgen/internal/strings/strings.hpp" + +namespace sqlgen::postgres { + +rfl::Result> Connection::make( + const Credentials& _credentials) noexcept { + try { + return Ref(Ref::make(_credentials)); + } catch (std::exception& e) { + return error(e.what()); + } +} + +typename Connection::ConnPtr Connection::make_conn( + const std::string& _conn_str) { + const auto raw_ptr = PQconnectdb(_conn_str.c_str()); + + if (PQstatus(raw_ptr) != CONNECTION_OK) { + const auto msg = std::string("Connection to postgres failed: ") + + PQerrorMessage(raw_ptr); + PQfinish(raw_ptr); + throw std::runtime_error(msg.c_str()); + } + + return ConnPtr::make(std::shared_ptr(raw_ptr, &PQfinish)).value(); +} + +} // namespace sqlgen::postgres diff --git a/src/sqlgen/postgres/Iterator.cpp b/src/sqlgen/postgres/Iterator.cpp new file mode 100644 index 0000000..c0c5475 --- /dev/null +++ b/src/sqlgen/postgres/Iterator.cpp @@ -0,0 +1,59 @@ +#include "sqlgen/sqlite/Iterator.hpp" + +#include +#include +#include + +#include "sqlgen/internal/collect/vector.hpp" +#include "sqlgen/internal/strings/strings.hpp" +#include "sqlgen/sqlite/Iterator.hpp" + +namespace sqlgen::sqlite { + +Iterator::Iterator(const StmtPtr& _stmt, const ConnPtr& _conn) + : end_(false), + rownum_(0), + num_cols_(sqlite3_column_count(_stmt.get())), + stmt_(_stmt), + conn_(_conn) { + step(); +} + +Iterator::~Iterator() = default; + +bool Iterator::end() const { return end_; } + +Result>>> Iterator::next( + const size_t _batch_size) { + if (end()) { + return error("End is reached."); + } + + std::vector>> batch; + + for (size_t i = 0; i < _batch_size; ++i) { + std::vector> new_row; + + for (int j = 0; j < num_cols_; ++j) { + auto ptr = sqlite3_column_text(stmt_.get(), j); + if (ptr) { + new_row.emplace_back( + std::string(std::launder(reinterpret_cast(ptr)))); + } else { + new_row.emplace_back(std::nullopt); + } + } + + batch.emplace_back(std::move(new_row)); + + step(); + + if (end()) { + return batch; + } + } + + return batch; +} + +} // namespace sqlgen::sqlite