/** Implementation of the pqxx::connection and sibling classes. * * Different ways of setting up a backend connection. * * Copyright (c) 2000-2019, Jeroen T. Vermeulen. * * See COPYING for copyright license. If you did not receive a file called * COPYING with this source code, please notify the distributor of this mistake, * or contact the author. */ #include "pqxx/compiler-internal.hxx" #include <stdexcept> extern "C" { #include "libpq-fe.h" } #include "pqxx/connection" pqxx::connectionpolicy::connectionpolicy(const std::string &opts) : m_options{opts} { } pqxx::connectionpolicy::~connectionpolicy() noexcept { } pqxx::connectionpolicy::handle pqxx::connectionpolicy::normalconnect(handle orig) { if (orig) return orig; orig = PQconnectdb(options().c_str()); if (orig == nullptr) throw std::bad_alloc{}; if (PQstatus(orig) != CONNECTION_OK) { const std::string msg{PQerrorMessage(orig)}; PQfinish(orig); throw broken_connection{msg}; } return orig; } pqxx::connectionpolicy::handle pqxx::connectionpolicy::do_startconnect(handle orig) { return orig; } pqxx::connectionpolicy::handle pqxx::connectionpolicy::do_completeconnect(handle orig) { return orig; } pqxx::connectionpolicy::handle pqxx::connectionpolicy::do_dropconnect(handle orig) noexcept { return orig; } pqxx::connectionpolicy::handle pqxx::connectionpolicy::do_disconnect(handle orig) noexcept { orig = do_dropconnect(orig); if (orig) PQfinish(orig); return nullptr; } bool pqxx::connectionpolicy::is_ready(handle h) const noexcept { return h != nullptr; } pqxx::connectionpolicy::handle pqxx::connect_direct::do_startconnect(handle orig) { if (orig) return orig; orig = normalconnect(orig); if (PQstatus(orig) != CONNECTION_OK) { const std::string msg{PQerrorMessage(orig)}; do_disconnect(orig); throw broken_connection{msg}; } return orig; } pqxx::connectionpolicy::handle pqxx::connect_lazy::do_completeconnect(handle orig) { return normalconnect(orig); } pqxx::connect_async::connect_async(const std::string &opts) : connectionpolicy{opts}, m_connecting{false} { } pqxx::connectionpolicy::handle pqxx::connect_async::do_startconnect(handle orig) { if (orig != nullptr) return orig; // Already connecting or connected. m_connecting = false; orig = PQconnectStart(options().c_str()); if (orig == nullptr) throw std::bad_alloc{}; if (PQstatus(orig) == CONNECTION_BAD) { do_dropconnect(orig); throw broken_connection{std::string{PQerrorMessage(orig)}}; } m_connecting = true; return orig; } pqxx::connectionpolicy::handle pqxx::connect_async::do_completeconnect(handle orig) { const bool makenew = (orig == nullptr); if (makenew) orig = do_startconnect(orig); if (not m_connecting) return orig; // Our "attempt to connect" state ends here, for better or for worse m_connecting = false; PostgresPollingStatusType pollstatus = PGRES_POLLING_WRITING; do { switch (pollstatus) { case PGRES_POLLING_FAILED: if (makenew) do_disconnect(orig); throw broken_connection{std::string{PQerrorMessage(orig)}}; case PGRES_POLLING_READING: internal::wait_read(orig); break; case PGRES_POLLING_WRITING: internal::wait_write(orig); break; case PGRES_POLLING_OK: break; default: // Meaningless, really, but deals with the obsolete PGRES_POLLING_ACTIVE // without requiring it to be defined. break; } pollstatus = PQconnectPoll(orig); } while (pollstatus != PGRES_POLLING_OK); return orig; } pqxx::connectionpolicy::handle pqxx::connect_async::do_dropconnect(handle orig) noexcept { m_connecting = false; return orig; } bool pqxx::connect_async::is_ready(handle h) const noexcept { return h != nullptr and not m_connecting; }