Skip to content
This repository has been archived by the owner. It is now read-only.
The PostgreSQL client API in modern C++
Branch: master
Clone or download
Latest commit 577c364 Jul 19, 2019
Type Name Latest commit message Commit time
Failed to load latest commit information.
cmake CMake stuff: refactoring May 23, 2019
doc Refactoring: dmitigr::internal -> dmitigr::common May 11, 2019
lib/dmitigr Header-only mode (fix on Linux) Jul 1, 2019
tests Header-only mode and some API changes Jul 1, 2019
.gitattributes Fix of .gitattributes May 22, 2018
.gitignore Add ignored internal/dll.hpp Feb 4, 2019
Doxyfile Refactoring: dmitigr::internal -> dmitigr::common May 11, 2019
LICENSE.txt Updated LICENSE.txt to provoke the correct license detection May 25, 2018 Moved to Cefeika Jul 19, 2019


The C++ client library for PostgreSQL {#mainpage}

The Dmitigr Pgfe (PostgreSQL Frontend, hereinafter referred to as Pgfe) is a client API to PostgreSQL servers written in C++. The development is focused on easines and robustness of use. At the same time, everything possible is being done to ensure that the performance is at its best. ATTENTION, this software is "beta" quality, and the API is a subject to change! Any feedback (especially results of testing) is highly appreciated! Together we can make Pgfe library really production-ready!

Please note, this tutorial can also be viewed at the official Pgfe documentation site. Because of the Pgfe documentation is generated by Doxygen, most of references to the classes and methods on the official documentation site are clickable, which makes the familiarization more convenient. Also there are overview class diagram of the API for better understanding.

Hello, World

#include <dmitigr/pgfe.hpp>
#include <iostream>

int main()
  namespace pgfe = dmitigr::pgfe;
  try {
    const auto conn = pgfe::Connection_options::make(pgfe::Communication_mode::net)->

    conn->execute("SELECT generate_series($1::int, $2::int) AS natural", 1, 3);
    conn->for_each([](const auto* const row) {
      std::cout << pgfe::to<int>(row->data("natural")) << "\n";
    std::cout << "The " << conn->completion()->operation_name() << " query is done.\n";

    // As a sample of error handling let's provoke syntax error and handle it away.
    try {
      conn->perform("PROVOKE SYNTAX ERROR");
    } catch (const pgfe::Server_exception& e) {
      if (e.error()->code() == pgfe::Server_errc::c42_syntax_error)
        std::cout << "Error " << e.error()->sqlstate() << " is handled as expected.\n";
  } catch (const std::exception& e) {
    std::cerr << "Oops: " << e.what() << "\n";


The Pgfe library can be used as:

  • a header-only library;
  • a static library;
  • a shared library (default).

Current API allows to work with:

  • database connections (in both blocking and non-blocking IO manner);
  • prepared statements (named parameters are supported);
  • SQLSTATE codes (as simple as with enums);
  • extensible data type conversions (including support of PostgreSQL arrays to/from STL containers conversions);
  • dynamic SQL;
  • SQL queries separately of C++ code.

Features of the near future

The urgent TODO-list includes support of:

  • exception class for each SQLSTATE code;
  • Large Objects via IO streams of the Standard C++ library;
  • the COPY command;
  • conversions for dmitigr::pgfe::Composite data type;
  • yet more convenient work with arrays of variable dimensions at runtime.


Client programs that use Pgfe should include header file dmitigr/pgfe.hpp and must link with dmitigr_pgfe (or the debug build - dmitigr_pgfed) library. Logically Pgfe library consists of the following parts:

  • Main (client/server communication);
  • Large objects (future feature, see the above TODO-list);
  • Data types conversions;
  • Errors (exceptions and error codes);
  • Utilities.

WARNING Headers other than dmitigr/pgfe.hpp should be avoided to use in applications since that headers are subject to reorganize. Also the namespace dmitigr::pgfe::detail consists of the implementation details and should not be used in the client code.

Connecting to a server

The class dmitigr::pgfe::Connection is a central abstraction of Pgfe library. By using methods of this class it is possible to:

  • send requests to a server;
  • receive responses from a server (see dmitigr::pgfe::Response);
  • receive signals from a server (see dmitigr::pgfe::Signal);
  • perform other operations that depend on a server data (such as dmitigr::pgfe::Connection::to_quoted_literal()).

To make an instance of the class dmitigr::pgfe::Connection, the instance of the class dmitigr::pgfe::Connection_options is required. A copy of this instance is always read-only accessible via dmitigr::pgfe::Connection::options().

Example 1. Creation of the connection with customized options:

std::unique_ptr<dmitigr::pgfe::Connection> create_customized_connection()
  return pgfe::Connection_options::make(Communication_mode::net)->

Example 2. Creation of the connection with default options:

std::unique_ptr<dmitigr::pgfe::Connection> create_default_connection_1()
  const auto opts = pgfe::Connection_options::make();
  return pgfe::Connection::make(opts.get());

Example 3. Creation of the connection with default options:

std::unique_ptr<dmitigr::pgfe::Connection> create_default_connection_2()
  return pgfe::Connection::make();

After creation of an object of type dmitigr::pgfe::Connection there are two ways to connect available:

  • synchronous by using dmitigr::pgfe::Connection::connect();
  • asynchronous by using dmitigr::pgfe::Connection::connect_async().

Executing commands

SQL commands can be executed through either of two ways:

  1. by using "simple query" protocol (which implies parsing and executing a query by a server on each request) with dmitigr::pgfe::Connection::perform();
  2. by using "extended query" protocol (which implies using of parameterizable prepared statements):
    • by explicitly preparing a statement with dmitigr::pgfe::Connection::prepare_statement() and executing it with dmitigr::pgfe::Prepared_statement::execute();
    • by implicitly preparing and executing an unnamed prepared statement with dmitigr::pgfe::Connection::execute().

Commands can be executed and processed asynchronously, i.e. without need of waiting a server response(-s), and thus, without thread blocking. For this purpose the methods of the class dmitigr::pgfe::Connection with the suffix _async shall be used, such as dmitigr::pgfe::Connection::perform_async() or dmitigr::pgfe::Connection::prepare_statement_async().

Prepared statements can be parameterized with either positional or named parameters. In order to use the named parameters, a SQL string must be preparsed by Pgfe. Preparsed SQL strings are represented by the class dmitigr::pgfe::Sql_string. Unparameterized prepared statements, or prepared statements parameterized by only positional parameters does not require to be preparsed. Thus, there is no need to create an instance of dmitigr::pgfe::Sql_string and std::string should be used instead.

To set a value of a prepared statement's parameter it should be converted to an object of the class dmitigr::pgfe::Data. For convenience, there is the templated method dmitigr::pgfe::Prepared_statement::set_parameter(std::size_t, T&&) which do such a conversion by using one of the specialization of the template structure dmitigr::pgfe::Conversions.

Example 1. Simple querying.

void simple_query(dmitigr::pgfe::Connection* conn)
  conn->perform("SELECT generate_series(1, 3) AS num");

Example 2. Implicit execution of the unnamed prepared statement.

void implicit_prepare_and_execute(dmitigr::pgfe::Connection* conn)
  conn->execute("SELECT generate_series($1::int, $2::int) AS num", 1, 3);

Example 3. Explicit execution of the named prepared statement with named parameters.

void explicit_prepare_and_execute(const std::string& name, dmitigr::pgfe::Connection* conn)
  static const auto sql = dmitigr::pgfe::Sql_string::make("SELECT generate_series(:infinum::int, :supremum::int) AS num");
  auto ps = conn->prepare_statement(sql.get(), name);
  ps->set_parameter("infinum",  1);
  ps->set_parameter("supremum", 3);

Responses handling

Server responses are represented by the classes, inherited from dmitigr::pgfe::Response:

  • Responses that are server errors are represented by the class dmitigr::pgfe::Error. Each server error is identifiable by a so-called SQLSTATE code. In Pgfe each such a code is represented by the member of the enum class dmitigr::pgfe::Server_errc, integrated in framework for reporting errors provided by the standard library in <system_error>. Therefore, working with SQLSTATE codes is as simple and safe as with std::error_code and enumerated types! For example:
void handle_error_example(dmitigr::pgfe::Connection* conn)
  try {
    conn->perform("PROVOKE SYNTAX ERROR");
  } catch (const dmitigr::pgfe::Server_exception& e) {
    assert(e.error()->code() == dmitigr::pgfe::Server_errc::c42_syntax_error);
  • Responses that are rows are represented by the class dmitigr::pgfe::Row. Objects of this class can be accessed by using dmitigr::pgfe::Connection::row() and/or dmitigr::pgfe::Connection::release_row(). However, it is best to use the method dmitigr::pgfe::Connection::for_each() for rows processing. Be aware, that before executing the subsequent operations, all of the rows must be processed.

  • Responses that are prepared statements are represented by the class dmitigr::pgfe::Prepared_statement. Prepared statements are accessible via the method dmitigr::pgfe::Connection::prepared_statement().

  • Responses that indicates success of operations are represented by the class dmitigr::pgfe::Completion. Such responses can be accessed by calling dmitigr::pgfe::Connection::completion() and/or dmitigr::pgfe::Connection::release_completion(). Alternatively, to process completion responses the method dmitigr::pgfe::Connection::complete() can be used.

To initiate asynchronous retrieving of the first response (i.e. with no blocking the thread), methods of the class dmitigr::pgfe::Connection with the suffix "_async" must be used. Otherwise, Pgfe will wait for the first response and if that response is dmitigr::pgfe::Error, an object of type dmitigr::pgfe::Server_exception will be thrown as exception. This object provides access to the retrieved object of type dmitigr::pgfe::Error, which contains the error details.

Server responses can be retrieved:

  • synchronously by using the methods such as dmitigr::pgfe::Connection::wait_response() and dmitigr::pgfe::Connection::wait_last_response();
  • asynchronously by using the methods such as dmitigr::pgfe::Connection::collect_server_messages() and dmitigr::pgfe::Connection::socket_readiness();

Data type conversions

Pgfe ships with support of conversions for fundamental and standard C++ types. Conversions for special PostgreSQL types such as Date/Time Types aren't provided out of the box, since many implementations of these types are possible at the client side. Instead it's up to the user to deside what implementation to use. (If such conversions are needed at all.) For example, the template structure dmitigr::pgfe::Conversions can be easily specialized to perform conversions between PostgreSQL Date/Time Types and types from the Boost.Date_Time library.

The class dmitigr::pgfe::Data is designed to store:

  • the values of prepared statements' parameters;
  • the data retrieved from PostgreSQL server.

The template structure dmitigr::pgfe::Conversions are used by:

  • dmitigr::pgfe::Prepared_statement::set_parameter(std::size_t, T&&) - to perfrom data conversions from objects or type T to objects of type dmitigr::pgfe::Data;
  • dmitigr::pgfe::to() - to perform data conversions from objects of type dmitigr::pgfe::Data to objects of the specified type T.

There is the partial specialization of the template structure dmitigr::pgfe::Conversions to perform conversions from/to PostgreSQL arrays representation to any combination of the STL containers. (At the moment, arrays conversions are only implemented for dmitigr::pgfe::Data_format::text format.) In general, any PostgreSQL array can be represented as Container<Optional<T>>, where:

  • Container - is the template class of the container such as std::vector or std::list or std::deque;
  • Optional - is the template class of the optional value holder such as std::optional or boost::optional. The special value like std::nullopt represents the SQL NULL;
  • T - is the type of elements of the array. It can be Container<Optional<T>> to represent the multidimensional array.

In case when all the array elements are non-NULL, such an array can be represented as just the container with elements of type T (ie no need to make it optional). But in case when the source array (which comes from the PostgreSQL server) contain at least one NULL element a runtime exception will be thrown.

In light of the above:

  • the types Container<Optional<T>>, Container<Optional<Container<Optional<T>>>>, ... can be used to represent N-dimensional arrays of T which can contain NULL values;

  • the types Container<T>, Container<Container<T>>, ... can be used to represent N-dimensional arrays of T which cannot contain NULL values;

User-defined data conversions could be implemented by either:

  • implementing the overloads of operator<< and operator>> for std::ostream and std::istream respectively;
  • specializing the template structure dmitigr::pgfe::Conversions. (With this approach overheads of standard IO streams can be avoided.)

Signals handling

Server signals are represented by the classes, inherited from dmitigr::pgfe::Signal:

  • signals that are server notices are represented by the class dmitigr::pgfe::Notice;
  • signals that are server notifications are represented by the class dmitigr::pgfe::Notification.

Signals can be handled:

  • synchronously, by using the signal handlers (see dmitigr::pgfe::Connection::set_notice_handler(), dmitigr::pgfe::Connection::set_notification_handler());
  • asynchronously, by using the methods that provides access to the retrieved signals directly (see dmitigr::pgfe::Connection::notice(), dmitigr::pgfe::Connection::notification()).

Signal handlers, being set, called by dmitigr::pgfe::Connection::handle_signals(). The latter is called automatically while waiting a response. If no handler is set, corresponding signals will be collected in the internal storage and can be popped up by using dmitigr::pgfe::Connection::pop_notice() and/or dmitigr::pgfe::Connection::pop_notification() depending on the type of signal.

WARNING If signals are not popped up from the internal storage it may cause memory exhaustion! Thus, signals must be handled anyway!

Dynamic SQL

The standard tools like std::string or std::ostringstream can be used to make SQL strings dynamically. However, in some cases it is more convenient to use the class dmitigr::pgfe::Sql_string for this purpose.

Consider the following statement:

auto sql = dmitigr::pgfe::Sql_string::make("SELECT :expr::int, ':expr'");

This statement has one named parameter expr and one string constant ':expr'. If prepare this statement with dmitigr::pgfe::Connection::prepare_statement(), the actual prepared statement parsed by the server will looks like:

SELECT $1::int, ':expr'

Before preparing the statement, it is possible to replace the named parameters of the SQL string with another SQL string by using dmitigr::pgfe::Sql_string::replace_parameter(). For example:

auto sql = dmitigr::pgfe::Sql_string::make("SELECT :expr::int, ':expr'");
sql->replace_parameter("expr", "sin(:expr1::int), cos(:expr2::int)");

Now the statement has two named parameters, and looks like:

SELECT sin(:expr1::int), cos(:expr2::int), ':expr'

Note, that the quoted string :expr is not affected by the replacement operation!

Working with SQL queries separately of C++ code

The idea of the approach is to store the SQL queries in the separate place, such as a text file. First, the content of this file must be read into the object of type std::string. Next, the object of type dmitigr::pgfe::Sql_vector should be created with dmitigr::pgfe::Sql_vector::make(). Finally, the required SQL string can be accessed by the index or by the extra data, such as, for example, the SQL string identifier which was specified in advance in the related comments of the query. Let's consider the simple SQL input:

-- This is query 1
-- $id$plus-one$id$
SELECT :n::int + 1, ';'; -- note, the semicolons in quotes are allowed!

/* This is query 2
 * $id$minus-one$id$
SELECT :n::int - 1

The vector of two SQL strings can be created from this input with dmitigr::pgfe::Sql_vector::make(). These objects has an extra data specified by the dollar-quoted string constants. (The dollar quoting syntax are well-know way of quoting string literals in PostgreSQL.) Next, these queries can be easily accessed by using the dmitigr::pgfe::Sql_vector API, for example:

std::string read_file(const std::filesystem::path& path);

void foo()
  namespace pgfe = dmitigr::pgfe;
  const std::string input = read_file("bunch.sql");
  auto bunch = pgfe::Sql_vector::make(input);
  auto* minus_one = bunch->sql_string("id", "minus-one"); // SELECT :n::int - 1
  auto*  plus_one = bunch->sql_string("id",  "plus-one"); // SELECT :n::int + 1, ';'
  // Next, working with the queries ...


Pgfe may throw:

  • an instance of the type std::logic_error when:
    • API contract requirements are violated;
    • an assertion failure has occurred (it is possible only with the "debug" build of Pgfe);
  • an instance of the types std::runtime_error or dmitigr::pgfe::Client_exception when some kind of runtime error occured on the client side;
  • the instance of the type dmitigr::pgfe::Server_exception when some error occured on the server side and the methods like dmitigr::pgfe::Connection::wait_response_throw() is in use (which is case when using dmitigr::pgfe::Connection::perform(), dmitigr::pgfe::Connection::execute() etc).

Thread safety

By default, if not explicitly documented, all functions and methods of Pgfe are not thread safe. Thus, in most cases, some of the synchronization mechanisms (like mutexes) must be used to work with the same object from several threads.


The Pgfe documentation is located at the official site here.


The Pgfe repository is located at Github here.

Installation and consuming


Build time settings

Settings that may be specified at build time by using CMake variables are:

  1. the type of the build (only meaningful to single-configuration generators);
  2. the flag to build the shared library (default is on);
  3. the flag to only install the header-only library (default is off);
  4. the flag to build the tests (default is on);
  5. dependencies;
  6. installation directories;
  7. default values of the connection options.

Details (may need to use horizontal scrolling for full view):

CMake variable Possible values Default on Unix Default on Windows
The type of the build
CMAKE_BUILD_TYPE Debug | Release | RelWithDebInfo | MinSizeRel Debug Debug
The flag to build the shared library
The flag to only install the header-only library
The flag to build the tests
LIBPQ_PREFIX a path not set (rely on CMake) not set (rely on CMake)
Installation directories
CMAKE_INSTALL_PREFIX an absolute path "/usr/local" "%ProgramFiles%\dmitigr_pgfe"
DMITIGR_PGFE_CMAKE_INSTALL_DIR a path relative to CMAKE_INSTALL_PREFIX "share/dmitigr_pgfe/cmake" "cmake"
DMITIGR_PGFE_DOC_INSTALL_DIR a path relative to CMAKE_INSTALL_PREFIX "share/dmitigr_pgfe/doc" "doc"
DMITIGR_PGFE_INCLUDE_INSTALL_DIR a path relative to CMAKE_INSTALL_PREFIX "include" "include"
Default values of the connection options
DMITIGR_PGFE_CONNECTION_UDS_DIRECTORY an absolute path /tmp unavailable
DMITIGR_PGFE_CONNECTION_TCP_KEEPALIVES_IDLE non-negative number null (system default) null (system default)
DMITIGR_PGFE_CONNECTION_TCP_KEEPALIVES_INTERVAL non-negative number null (system default) null (system default)
DMITIGR_PGFE_CONNECTION_TCP_KEEPALIVES_COUNT non-negative number null (system default) null (system default)
DMITIGR_PGFE_CONNECTION_NET_HOSTNAME a string localhost localhost
DMITIGR_PGFE_CONNECTION_USERNAME a string postgres postgres
DMITIGR_PGFE_CONNECTION_DATABASE a string postgres postgres
DMITIGR_PGFE_CONNECTION_KERBEROS_SERVICE_NAME a string null (not used) null (not used)
DMITIGR_PGFE_CONNECTION_SSL_CERTIFICATE_FILE an absolute path null (libpq's default) null (libpq's default)
DMITIGR_PGFE_CONNECTION_SSL_PRIVATE_KEY_FILE an absolute path null (libpq's default) null (libpq's default)
DMITIGR_PGFE_CONNECTION_SSL_CERTIFICATE_AUTHORITY_FILE an absolute path null (libpq's default) null (libpq's default)
DMITIGR_PGFE_CONNECTION_SSL_CERTIFICATE_REVOCATION_LIST_FILE an absolute path null (libpq's default) null (libpq's default)

Installation in common

The only dependences of Pgfe are dmitigr_common and libpq.

First, dmitigr_common must be installed. The installation is trivial as described on its page.

WARNING It's highly recommended to update the dmitigr_common library (just pull and reinstall) before building the Pgfe library!

As for libpq, CMake will try to locate it automatically. Although, it's possible to manually specify the location of libpq by using the following CMake variables:

  • LIBPQ_PREFIX - can be used to speficy a prefix for both binary and headers of libpq. For example, if PostgreSQL installed relocatably into /usr/local/pgsql, the value of LIBPQ_PREFIX may be set accordingly at command line such as: -DLIBPQ_PREFIX=/usr/local/pgsql;
  • LIBPQ_LIB_PREFIX - similar to the above, but specifies a prefix of the libpq binary file (SO or DLL);
  • LIBPQ_INCLUDE_PREFIX - similar to the above, but specifies a prefix of the libpq headers (namely, libpq-fe.h).

Installation on Linux

$ git clone
$ mkdir -p pgfe/build
$ cd pgfe/build
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
$ make
$ sudo make install

The value of the CMAKE_BUILD_TYPE could be replaced. Also, remember about the possibility to specify location of libpq if CMake could not detect it automatically (see "Installation in common" section above).

Installation on Microsoft Windows

Run the Developer Command Prompt for Visual Studio and type:

> git clone
> mkdir pgfe\build
> cd pgfe\build
> cmake -G "Visual Studio 15 2017 Win64" ..
> cmake --build . --config Debug

Next, run the Elevated Command Prompt (i.e. the command prompt with administrator privileges) and type:

> cd pgfe\build
> cmake -DBUILD_TYPE=Debug -P cmake_install.cmake

If the target architecture is Win32 or ARM, then "Win64" should be replaced by "Win32" or "ARM" accordingly.

To make the installed DLL available for any application that depends on it, the symbolic link to the dmitigr_pgfe.dll (or to the debug version - dmitigr_pgfed.dll) should be created:

  • in %SYSTEMROOT%\System32 for the 64-bit DLL on 64-bit host (or for 32-bit DLL on 32-bit host);
  • in %SYSTEMROOT%\SysWOW64 for the 32-bit DLL on 64-bit host.

To create the symbolic link run the Elevated Command Prompt and use mklink command, for example:

> cd /d %SYSTEMROOT%\System32
> mklink dmitigr_pgfed.dll "%ProgramFiles%\dmitigr_pgfe\lib\dmitigr_pgfed.dll"

The value of the BUILD_TYPE could be replaced. Also, remember about the possibility to specify location of libpq if CMake could not detect it automatically (see "Installation in common" section above).

WARNING The target architecture must corresponds to the bitness of libpq to link!


If you are using CMake the consuming of the Pgfe library is quite simple. For example:

cmake_minimum_required(VERSION 3.13)
find_package(dmitigr_pgfe REQUIRED)
add_executable(foo foo.cpp)
target_link_libraries(foo dmitigr_pgfe)

The above code snippet is minimal CMakeLists.txt that enough to build the application foo that depends on the Pgfe library.

To consume the header-only version of the Pgfe library just specify it by using CONFIGS option of find_package:

find_package(dmitigr_ttpl REQUIRED CONFIGS dmitigr_ttpl_interface-config.cmake)
# ...
target_link_libraries(foo dmitigr_ttpl_interface)

It's possible to use header-only version of the Pgfe library without CMake (still need to link with libpq manually). Just include the path where dmitigr/pgfe and dmitigr/pgfe.hpp are located to the include directories path and define the DMITIGR_PGFE_HEADER_ONLY macro just before including of dmitigr/pgfe.hpp:

#include <dmitigr/pgfe.hpp>
// ...


Pgfe library is distributed under zlib license. For conditions of distribution and use, see files LICENSE.txt or pgfe.hpp.

Contributions, sponsorship, partnership

Pgfe has been developed on the own funds. Donations are welcome!

If you are using Pgfe for commercial purposes it is reasonable to donate or even sponsor the further development of Pgfe.

To make a donation, via PayPal please go here or here.

If you need a commercial support, or you need to develop a custom client-side or server-side software based on PostgreSQL, please contact us by sending email to

Pgfe is a free software. Enjoy using it!


Any feedback are welcome. Contact us by sending email to


Copyright (C) Dmitry Igrishin

You can’t perform that action at this time.