{"id":13757176,"url":"https://github.com/dmitigr/pgfe","last_synced_at":"2025-05-10T05:31:50.093Z","repository":{"id":41896494,"uuid":"134432885","full_name":"dmitigr/pgfe","owner":"dmitigr","description":"PostgreSQL C++ driver","archived":false,"fork":false,"pushed_at":"2025-04-16T20:23:38.000Z","size":1617,"stargazers_count":165,"open_issues_count":4,"forks_count":19,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-04-17T07:30:21.614Z","etag":null,"topics":["cpp","postgresql","postgresql-client","postgresql-driver"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dmitigr.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-05-22T15:05:48.000Z","updated_at":"2025-04-16T21:01:26.000Z","dependencies_parsed_at":"2024-08-03T12:01:15.231Z","dependency_job_id":"61f00938-ff51-41f2-9332-312ab39f2273","html_url":"https://github.com/dmitigr/pgfe","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmitigr%2Fpgfe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmitigr%2Fpgfe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmitigr%2Fpgfe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmitigr%2Fpgfe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dmitigr","download_url":"https://codeload.github.com/dmitigr/pgfe/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253371072,"owners_count":21897998,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","postgresql","postgresql-client","postgresql-driver"],"created_at":"2024-08-03T12:00:27.966Z","updated_at":"2025-05-10T05:31:49.693Z","avatar_url":"https://github.com/dmitigr.png","language":"C++","readme":"# [PostgreSQL] C++ driver (PostgreSQL client API)\n\n`dmitigr::pgfe` (*PostGres FrontEnd*) - is an advanced, feature rich and\ncross-platform [PostgreSQL] driver written in C++. The development is focused\non easines and robustness of use with the performance in mind.\n\n## Hello, World\n\n```cpp\n#include \u003cdmitigr/pgfe/pgfe.hpp\u003e\n\n#include \u003ccstdio\u003e\n\nnamespace pgfe = dmitigr::pgfe;\n\nint main() try {\n  // Making the connection.\n  pgfe::Connection conn{pgfe::Connection_options{}\n    .set(pgfe::Communication_mode::net)\n    .set_hostname(\"localhost\")\n    .set_database(\"pgfe_test\")\n    .set_username(\"pgfe_test\")\n    .set_password(\"pgfe_test\")};\n\n  // Connecting.\n  conn.connect();\n\n  // Using Pgfe's helpers.\n  using pgfe::a;  // for named arguments\n  using pgfe::to; // for data conversions\n\n  // Executing statement with positional parameters.\n  conn.execute([](auto\u0026\u0026 r)\n  {\n    std::printf(\"Number %i\\n\", to\u003cint\u003e(r.data()));\n  }, \"select generate_series($1::int, $2::int)\", 1, 3);\n\n  // Execute statement with named parameters.\n  conn.execute([](auto\u0026\u0026 r)\n  {\n    std::printf(\"Range [%i, %i]\\n\", to\u003cint\u003e(r[\"b\"]), to\u003cint\u003e(r[\"e\"]));\n  },\"select :begin b, :end e\", a{\"end\", 1}, a{\"begin\", 0});\n\n  // Prepare and execute the statement.\n  auto ps = conn.prepare(\"select $1::int i\");\n  for (int i{}; i \u003c 3; ++i)\n    ps.execute([](auto\u0026\u0026 r){std::printf(\"%i\\n\", to\u003cint\u003e(r[\"i\"]));}, i);\n\n  // Invoking the function.\n  conn.invoke([](auto\u0026\u0026 r)\n  {\n    std::printf(\"cos(%f) = %f\\n\", .5f, to\u003cfloat\u003e(r.data()));\n  }, \"cos\", .5f);\n\n  // Provoking the syntax error.\n  conn.execute(\"provoke syntax error\");\n } catch (const pgfe::Server_exception\u0026 e) {\n  assert(e.error().condition() == pgfe::Server_errc::c42_syntax_error);\n  std::printf(\"Error %s is handled as expected.\\n\", e.error().sqlstate());\n } catch (const std::exception\u0026 e) {\n  std::printf(\"Oops: %s\\n\", e.what());\n  return 1;\n }\n```\n\n## Features\n\n  - fast and robust;\n  - can be used as either header-only, static or shared library;\n  - works with database connections in both blocking and non-blocking IO manner;\n  - supports prepared statements with both positional and named parameters;\n  - provides first-class support for calling functions and procedures;\n  - supports advanced features of PostgreSQL, such as [pipeline], [COPY][copy]\n  and [large objects][lob];\n  - supports advanced error handling via exceptions and error conditions:\n  provides enum entry for each predefined [SQLSTATE][errcodes];\n  - provides advanced support for the client/server data conversion: even\n  multidimensional [PostgreSQL] arrays to/from any combinations of STL\n  containers can be performed with easy;\n  - provides a support of dynamic construction of SQL queries;\n  - allows to separate SQL queries and C++ code on the client side;\n  - provides a simple, robust and thread-safe connection pool;\n  - provides a transaction guard facility.\n\n## Requirements\n\n  - C++17 compiler (tested on GCC and MSVC);\n  - [libpq] library;\n  - [CMake] 3.16+ (optional, if build is required);\n  - [Doxygen] 1.9+ (optional, if the documentation generation is required).\n\n## Usage\n\n### Quick usage as header-only library\n\nCopy the contents of the `src` directory to a project directory which is under\nan include path of a compiler, for example, `src/3rdparty/dmitigr`.\n\nCreate `hello.cpp`:\n\n```C++\n#include \"dmitigr/pgfe/pgfe.hpp\"\n\nint main()\n{\n  dmitigr::pgfe::Connection conn;\n}\n```\n\nCompile `hello.cpp`:\n\n```\ng++ -std=c++17 -I/usr/local/pgsql/include -L/usr/local/pgsql/lib -lpq -ohello hello.cpp\n```\n\n### Quick usage with CMake\n\nCreate build directory, configure, build and install (note, if libpq is in a\nnon-standard location, then the path to it can be specified by using\n`-DPq_ROOT` as shown below):\n\n```\ncd pgfe\nmkdir build \u0026\u0026 cd build\ncmake -DPq_ROOT=/usr/local/pgsql .. # -DPq_ROOT is optional\ncmake --build .\nsudo cmake --install .\n```\n\nCreate `hello/hello.cpp`:\n\n```C++\n#include \"pgfe/pgfe.hpp\"\n\nint main()\n{\n  dmitigr::pgfe::Connection conn;\n}\n```\n\nCreate `hello/CMakeLists.txt`:\n\n```cmake\ncmake_minimum_required(VERSION 3.16)\nproject(foo)\nfind_package(dmitigr_libs REQUIRED COMPONENTS pgfe)\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nadd_executable(hello hello.cpp)\ntarget_link_libraries(hello dmitigr_pgfe)\n```\n\nCompile `hello/hello.cpp`:\n\n```\nmkdir hello/build \u0026\u0026 cd hello/build\ncmake ..\ncmake --build .\n```\n\n## Quick tutorial\n\nLogically, Pgfe library consists of the following parts:\n\n  - main (client/server communication);\n  - data types conversions;\n  - errors (exceptions and error conditions);\n  - utilities.\n\nThe API is defined in the namespace `dmitigr::pgfe`. In this tutorial all the\nnames are not explicitly qualified by this namespace.\n\n### Connecting to a server\n\nBy using class `Connection_options` it's easy to specify the required connection\noptions:\n\n```cpp\n// Example 1. Making connection options.\nauto make_options()\n{\n  return Connection_options{}\n    .set(Communication_mode::net)\n    .set_hostname(\"localhost\")\n    .set_database(\"db\")\n    .set_username(\"user\")\n    .set_password(\"password\");\n}\n```\n\nBy using class `Connection` it's easy to connect to the PostgreSQL server:\n\n```cpp\n// Example 2. Making ready-to-use connection.\nauto make_connection(const Connection_options\u0026 opts = {})\n{\n  Connection conn{opts};\n  conn.connect(); // connect synchronously (in blocking manner)\n  return conn; // return the ready-to-use instance\n}\n```\n\n### Executing SQL commands\n\nOnly extended query protocol is used under the hood of Pgfe. SQL commands can be\nexecuted and processed either synchronously or in non-blocking IO maner, i.e.\nwithout need of waiting for server response(-s). In the latter case the methods\nof the class `Connection` with the suffix `_nio` shall be used.\n\nWith Pgfe it's easy to execute single commands:\n\n```cpp\n// Example 3. Executing single commands.\nvoid foo(Connection\u0026 conn)\n{\n  conn.execute(\"begin\");\n  conn.execute(\"create temp table num(val integer not null)\");\n  conn.execute([](auto\u0026\u0026 row)\n  {\n    using dmitigr::pgfe::to;    // see \"Data conversions\" section for details\n    auto val = to\u003cint\u003e(row[0]); // converts the value of num.val to int\n    std::cout \u003c\u003c val \u003c\u003c \"\\n\";   // prints the just inserted integers\n  }, \"insert into num select generate_series(1,3) returning val\");\n  conn.execute(\"rollback\");\n}\n```\n\nExtended query protocol used by Pgfe is based on prepared statements. In Pgfe\nprepared statements can be parameterized with both positional and named\nparameters. The class `Statement` provides functionality for constructing SQL\nstatements, providing support for named parameters, as well as functionality for\ndirect parameters replacement with any SQL statement to generate complex SQL\nexpressions dynamically.\n\nIn most cases unnamed statements prepared implicitly:\n\n```cpp\n// Example 4. Preparing unnamed statements (`BEGIN`, `SELECT`, `ROLLBACK`) implicitly.\nvoid foo(Connection\u0026 conn)\n{\n  conn.execute(\"begin\");\n  conn.execute([](auto\u0026\u0026 row)\n  {\n    using dmitigr::pgfe::to;            // see \"Data conversions\" section for details\n    auto val = to\u003cstd::string\u003e(row[0]); // converts the retrieved value to std::string\n    std::cout \u003c\u003c val \u003c\u003c \"\\n\";           // prints \"Hi!\\n\"\n  }, \"select 'Hi!'\");\n  conn.execute(\"rollback\");\n}\n```\n\nIt's also easy to use named parameters:\n\n```cpp\n// Example 5. Using named parameters in statements.\nvoid foo(Connection\u0026 conn)\n{\n  // Please note, the sequence of the specified named parameters doesn't matter,\n  // and that \"end\" parameter is specified before \"begin\" parameter.\n  using dmitigr::pgfe::a;\n  conn.execute([](auto\u0026\u0026 row)\n  {\n    std::printf(\"Range [%i, %i]\\n\", to\u003cint\u003e(row[\"b\"]), to\u003cint\u003e(row[\"e\"]));\n  },\"select :begin b, :end e\", a{\"end\", 1}, a{\"begin\", 0});\n}\n```\n\nOf course, the statement (named or unnamed) can be prepared explicitly:\n\n```cpp\n// Example 6. Preparing the unnamed statement parameterized by named parameters.\nvoid foo(Connection\u0026 conn)\n{\n  conn.prepare(\"select generate_series(:inf::int, :sup::int) num\")\n    .bind(\"inf\", 1)\n    .bind(\"sup\", 3)\n    .execute([](auto\u0026\u0026 row)\n    {\n      // Printing the just generated integers without type conversion\n      std::printf(\"%s\\n\", row[\"num\"].bytes());\n    });\n}\n```\n\n### Invoking functions and calling procedures\n\nPgfe provides the convenient API for functions invoking or procedures calling:\nmethods `Connection::invoke()`, `Connection::invoke_unexpanded()` and\n`Connection::call()` accordingly.\n\nTo illustrate the API the following function definition is used:\n\n```sql\ncreate function person_info(id integer, name text, age integer)\nreturns text language sql as\n$$\n  select format('id=%s name=%s age=%s', id, name, age);\n$$;\n```\n\nCalling \"person_info\" by using positional notation:\n\n```cpp\n// Example 7. Using positional notation.\nvoid foo(Connection\u0026 conn)\n{\n  conn.invoke(\"person_info\", 1, \"Dmitry\", 36);\n  // ...\n}\n```\n\nCalling \"person_info\" by using named notation:\n\n```cpp\n// Example 8. Using named notation.\nvoid foo(Connection\u0026 conn)\n{\n  using dmitigr::pgfe::a;\n  conn.invoke(\"person_info\", a{\"name\", \"Dmitry\"}, a{\"age\", 36}, a{\"id\", 1});\n  // ...\n}\n```\n\nCalling \"person_info\" by using mixed notation:\n\n```cpp\n// Example 9. Using mixed notation.\nvoid foo(Connection\u0026 conn)\n{\n  using dmitigr::pgfe::a;\n  conn.invoke(\"person_info\", 1, a{\"age\", 36}, a{\"name\", \"Dmitry\"});\n  // ...\n}\n```\n\n### Data conversions\n\nBy default, Pgfe provides the support of the data conversions for *fundamental\nand standard C++ types* only. Conversions for special PostgreSQL types such as\n[Date/Time Types][datatype-datetime] aren't provided out of the box, since many\nimplementations of these types are possible at the client side. Instead it's up\nto the application developers to decide what implementation to use. (If such\nconversions are needed at all.) For example, the template structure `Conversions`\ncan be easily specialized to convert the data between PostgreSQL\n[Date/Time Types][datatype-datetime] and types from the\n[Boost.Date_Time][boost_datetime] library.\n\nThe abstract class `Data` is designed to provide the interface for:\n\n  - the values of prepared statements' parameters;\n  - the data retrieved from a [PostgreSQL] server.\n\nThe template structure `Conversions` is used by:\n\n  - method `Prepared_statement::bind(std::size_t, T\u0026\u0026)` to perfrom data\n  conversions from objects or type `T` to objects of type `Data`;\n  - function `to()` to perform data conversions from objects of type `Data`\n  to objects of the specified type `T`.\n\nPgfe provides the partial specialization of the template structure `Conversions` to\nconvert from/to [PostgreSQL] arrays (*including multidimensional arrays!*)\nrepresentation to **any combination of the STL containers** out of the box! (At the\nmoment, arrays conversions are only implemented for `Data_format::text` format.) In\ngeneral, *any* [PostgreSQL] array can be represented as `Container\u003cOptional\u003cT\u003e\u003e`,\nwhere:\n\n  - `Container` - is a template class of a container such as\n    [`std::vector`][std_vector] or [`std::list`][std_list] or [`std::deque`][std_deque];\n  - `Optional` - is a template class of an optional value holder such as\n    [`std::optional`][std_optional] or [`boost::optional`][boost_optional]. The\n    special value like [`std::nullopt`][std_nullopt] represents the SQL `NULL`;\n  - `T` - is the type of elements of the array. It can be `Container\u003cOptional\u003cU\u003e\u003e`\n    to represent the multidimensional array.\n\nIn case when all the elements of the array are not `NULL`, it *can* be represented\nas the container with elements of type `T` rather than `Optional\u003cT\u003e`. But in case\nwhen the source array (which comes from the PostgreSQL server) contain at least\none `NULL` element a `Client_exception` will be thrown. Summarizing:\n\n  - the types `Container\u003cOptional\u003cT\u003e\u003e`, `Container\u003cOptional\u003cContainer\u003cOptional\u003cT\u003e\u003e\u003e\u003e`,\n    `...` can be used to represent N-dimensional arrays of `T` which *may* contain `NULL`\n    values. (Pgfe provides the templated using definitions Array_optional1, ...,\n    Array_optional6 for convenience;)\n\n  - the types `Container\u003cT\u003e`, `Container\u003cContainer\u003cT\u003e\u003e`, `...` can be used to represent\n    N-dimensional arrays of `T` which *may not* contain `NULL` values. (Pgfe provides\n    the templated using definitions Array1, ..., Array6 for convenience.)\n\nUser-defined data conversions could be implemented by either:\n\n  - overloading the operators `operator\u003c\u003c` and `operator\u003e\u003e` for\n    [`std::ostream`][std_ostream] and [`std::istream`][std_istream] respectively;\n  - specializing the template structure `Conversions`. (With this approach overheads\n    of standard IO streams can be avoided.)\n\n### Response processing\n\nServer responses can be retrieved:\n\n  - implicitly in blocking IO manner by using methods such as `Connection::prepare()`,\n  `Connection::execute()` etc. Some of these methods has overloads for passing\n  the callback which is called by Pgfe every time the row is retrieved from the\n  server;\n  - explicitly in blocking IO manner by using methods such as\n  `Connection::wait_response()` and `Connection::wait_response_throw()` etc after\n  the using methods with the suffix \"_nio\";\n  - explicitly in non-blocking IO maner by using the methods such as\n  `Connection::read_input()`, `Connection::handle_input()`,\n  `Connection::socket_readiness()` etc after the using methods with suffix \"_nio\".\n\nTo *initiate* retrieving the *first* response in non-blocking IO manner methods\nof the class `Connection` with the suffix `_nio` must be used. Otherwise, Pgfe\nwill wait for the *first* response and if that response is error, an instance of\ntype `Server_exception` will be thrown. This object provides access to the object\nof type `Error`, which contains all the error details.\n\nServer responses are represented by the classes inherited from `Response`:\n\n  - errors are represented by the class `Error`. Each server error is identifiable\n    by a [SQLSTATE][errcodes] condition. In Pgfe *each* such a condition is\n    represented by the member of the enumeration `Server_errc`, integrated to the\n    framework for reporting errors provided by the standard library in\n    [`\u003csystem_error\u003e`][system_error]. Therefore, working with [SQLSTATEs][errcodes]\n    is as simple and safe as with [`std::error_condition`][std_error_condition]\n    and enumerated types (see Example 10);\n  - rows are represented by the class `Row`, for example (see Example 11);\n  - prepared statements are represented by the class `Prepared_statement` (see\n  Example 12).\n  - operation success indicators are represented by the class `Completion` (see\n  Example 13).\n\n```cpp\n// Example 10. Catching the syntax error.\nvoid foo(Connection\u0026 conn)\n{\n  try {\n    conn.execute(\"provoke syntax error\");\n  } catch (const Server_exception\u0026 e) {\n    assert(e.error().condition() == Server_errc::c42_syntax_error);\n  }\n}\n```\n\n```cpp\n// Example 11. Processing the rows.\nvoid foo(Connection\u0026 conn)\n{\n  conn.execute([](auto\u0026\u0026 row)\n  {\n    using dmitigr::pgfe::to;\n    auto name = to\u003cstd::string\u003e(row[\"name\"]);\n    std::printf(\"%s\\n\", name.data());\n  }, \"select name from usr where id = $1\", 3); // where id = 3\n}\n```\n\n```cpp\n// Example 12. Working with named prepared statement.\nvoid foo(Connection\u0026 conn)\n{\n  // Prepare the named statement\n  auto int_gen = conn.prepare(\"select generate_series($1::int, $2::int)\", \"int_gen\");\n\n  // Defining the row processor\n  auto process = [](auto\u0026\u0026 row)\n  {\n    using dmitigr::pgfe::to;\n    auto n = to\u003cint\u003e(row[0]);\n    std::printf(\"%i\\n\", n);\n  };\n\n  // Execute for the first time\n  int_gen.bind(1).bind(2).execute(process);\n  // Execute for the second time\n  int_gen.bind(10).bind(20).execute(process);\n}\n```\n\n```cpp\n// Example 13. Using completion info.\nvoid foo(Connection\u0026 conn)\n{\n  auto completion = conn.execute(\"begin\");\n  std::printf(\"%s\\n\", completion.operation_name()); // prints \"BEGIN\"\n}\n```\n\n### Signal handling\n\nServer signals are represented by classes `Notice` and `Notification`, inherited\nfrom class `Signal`. Signals can be handled by using the signal handlers (see\n`Connection::set_notice_handler()` and `Connection::set_notification_handler()`).\nNotifications can also be handled in non-blocking IO maner, by using the method\n`Connection::pop_notification()`.\n\nSignal handlers, being set, called by Pgfe automatically when signals are retrieved.\n(Usually it happens upon waiting a response.) If no notification handler is set,\nnotifications will be queued to the internal storage until popped up by method\n`Connection::pop_notification()`. **Be aware, that if notification are not popped\nup from the internal storage it may cause memory exhaustion!**\n\n### Dynamic SQL\n\nThe standard classes like [`std::string`][std_string] or\n[`std::ostringstream`][std_ostringstream] can be used to make SQL strings\ndynamically. However, in some cases it is more convenient to use the class\n`Statement` for this purpose. Consider the following statement:\n\n```sql\nselect :expr::int, ':expr';\n```\n\nThis SQL string has one named parameter `expr` and one string constant `':expr'`.\nIt's possible to replace the named parameters of the SQL string with another SQL\nstring by using `Statement::replace_parameter()`, for example:\n\n```cpp\n// Example 14. Extending the SQL statement.\nvoid foo()\n{\n  Statement sql{\"select :expr::int, ':expr'\"};\n  sql.replace_parameter(\"expr\", \"sin(:expr1::int), cos(:expr2::int)\");\n}\n```\n\nNow the original statement is modified and has two named parameters:\n\n```sql\nselect sin(:expr1::int), cos(:expr2::int), ':expr'\n```\n\nNote, that the quoted string `:expr` is not affected by the replacement operation.\n\n### Working with SQL code separately of C++ code\n\nThis feature is based on the idea to store the SQL code in a separate place,\nsuch as a text file. Consider the following SQL input, which is consists of two\nSQL strings with an extra data specified by the [dollar-quoted][dollar-quoting]\nstring constants in the related comments:\n\n```sql\n-- This is query 1\n--\n-- $id$plus-one$id$\nselect :n::int + 1, ';'; -- note, the semicolons in quotes are allowed!\n\n/* This is query 2\n *\n * $id$minus-one$id$\n */\nselect :n::int - 1\n```\n\nThese SQL strings can be easily accessed by using class `Statement_vector`:\n\n```cpp\n// Example 15. Parsing file with SQL statements.\n\nstd::string read_file(const std::filesystem::path\u0026 path); // defined somewhere\n\nvoid foo()\n{\n  auto input = read_file(\"bunch.sql\");\n  Statement_vector bunch{input};\n  auto minus_pos = bunch.statement_index(\"id\", \"minus-one\"); // select :n::int - 1\n  auto plus_pos = bunch.statement_index(\"id\",  \"plus-one\"); // select :n::int + 1, ';'\n  // ...\n}\n```\n\n### Connection pool\n\nPgfe provides a simple connection pool implemented as class `Connection_pool`:\n\n```cpp\n// Example 16. Using the connection pool.\n\nConnection_options connection_options(); // defined somewhere.\n\nint main()\n{\n  Connection_pool pool{2, connection_options()};\n  pool.connect(); // open 2 connections\n  {\n    auto conn1 = pool.connection(); // 1st attempt to get the connection from pool\n    assert(conn1);  // ok\n    conn1.execute(\"select 1\");\n    auto conn2 = pool.connection(); // 2nd attempt to get the connection from pool\n    assert(conn2);  // ok\n    conn2.execute(\"select 2\");\n    auto conn3 = pool.connection(); // 3rd attempt to get the connection from pool\n    assert(!conn3); // the pool is exhausted\n  } // connections are returned back to the pool here\n  auto conn = pool.connection();\n  assert(conn); // ok\n  pool.disconnect(); // done with the pool\n}\n```\n\n### Transaction guard\n\nPgfe provides a convenient [RAII-style][raii] facility for owning a transaction\n(or subtransaction) for the duration of a scoped block. When control leaves the\nscope in which the `Transaction_guard` object was created, the Transaction_guard\nis destructed and the transaction is *rolled back*. If the rollback failed, the\nconnection is closed, to prevent further interaction with the database, since\nfailed rollback might indicate a total mess.\n\n```cpp\n// Example 17. Using the transaction guard.\n\nvoid foo(Connection\u0026 conn)\n{\n  assert(conn.is_connected() \u0026\u0026 !conn.is_transaction_uncommitted());\n  try {\n    Transaction_guard tg{conn}; // begin transaction\n    {\n      Transaction_guard tg{conn}; // begin subtransaction (define savepoint 1)\n      {\n        Transaction_guard tg{conn}; // begin subtransaction (define savepoint 2)\n        tg.commit(); // release savepoint 2\n      }\n      tg.commit(); // release savepoint 1\n    }\n    tg.commit_and_chain(); // commit transaction and immediately begin the next one\n    {\n      Transaction_guard tg{conn}; // begin subtransaction (define savepoint 1)\n      throw 1; // oops, attempt to rollback the entire transaction\n    }\n  } catch (...) {\n    assert(!conn.is_connected() || !conn.is_transaction_uncommitted());\n  }\n}\n```\n\n## Exceptions\n\nPgfe itself may throw:\n\n  - an instance of the type `Client_exception` when error originated on the\n  client side;\n  - an instance of the type `Server_exception` when error originated on the\n  server side upon using of IO blocking API.\n\n## Thread safety\n\nBy default, if not explicitly documented, all functions and methods of Pgfe are\n*not* thread safe. Thus, in most cases, some of the synchronization mechanisms\n(like mutexes) must be used to work with the same object from several threads.\n\n## Dependencies\n\nPgfe is depends on the [libpq] library.\n\n## CMake options\n\nPlease note:\n\n  - by default, `CMAKE_BUILD_TYPE` is set to `Release`;\n  - by using `Pq_ROOT` it's possible to specify a prefix for both binary and\n  headers of the [libpq]. For example, if [PostgreSQL] installed relocatably\n  into `/usr/local/pgsql`, the value of `Pq_ROOT` should be set accordingly;\n  - when building with Microsoft Visual Studio the value of `CMAKE_BUILD_TYPE`\n  doesn't selects the build configuration within the generated build environment.\n  The [CMake] command line option `--config` should be used for that purpose.\n\n[dmitigr_pgfe]: https://github.com/dmitigr/pgfe.git\n\n[PostgreSQL]: https://www.postgresql.org/\n[dollar-quoting]: https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING\n[datatype-datetime]: https://www.postgresql.org/docs/current/datatype-datetime.html\n[errcodes]: https://www.postgresql.org/docs/current/static/errcodes-appendix.html\n[libpq]: https://www.postgresql.org/docs/current/static/libpq.html\n[lob]: https://www.postgresql.org/docs/current/static/largeobjects.html\n[copy]: https://www.postgresql.org/docs/current/sql-copy.html\n[pipeline]: https://www.postgresql.org/docs/current/libpq-pipeline-mode.html\n\n[boost_datetime]: https://www.boost.org/doc/libs/release/libs/date_time/\n[boost_optional]: https://www.boost.org/doc/libs/release/libs/optional/\n[raii]: https://en.cppreference.com/w/cpp/language/raii\n\n[CMake]: https://cmake.org/\n[Doxygen]: http://doxygen.org/\n\n[system_error]: https://en.cppreference.com/w/cpp/header/system_error\n[std_deque]: https://en.cppreference.com/w/cpp/container/deque\n[std_error_condition]: https://en.cppreference.com/w/cpp/error/error_condition\n[std_istream]: https://en.cppreference.com/w/cpp/io/basic_istream\n[std_list]: https://en.cppreference.com/w/cpp/container/list\n[std_optional]: https://en.cppreference.com/w/cpp/utility/optional\n[std_ostringstream]: https://en.cppreference.com/w/cpp/io/basic_ostringstream\n[std_ostream]: https://en.cppreference.com/w/cpp/io/basic_ostream\n[std_nullopt]: https://en.cppreference.com/w/cpp/utility/optional/nullopt\n[std_string]: https://en.cppreference.com/w/cpp/string/basic_string\n[std_vector]: https://en.cppreference.com/w/cpp/container/vector\n","funding_links":[],"categories":["Database Clients/Drivers"],"sub_categories":["UI Test Automation Scripting"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmitigr%2Fpgfe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdmitigr%2Fpgfe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmitigr%2Fpgfe/lists"}