libpqxx Documentation

repository·master·Indexed 23 days ago

https://github.com/jtv/libpqxx

A high-level, type-safe C++ API for the PostgreSQL database management system, built on top of the standard PostgreSQL C API (libpq). It provides core classes such as pqxx::connection, pqxx::work for transactions, and pqxx::result for query metadata. Version 8.x and later require a C++20 compatible compiler. The library supports build systems including CMake and GNU autotools.

Tokens
12.3K
Snippets
25
Records
67
Agent score
80%

What's inside libpqxx

  1. Handle zero bytes in strings and binary data

    master

    Because libpqxx wraps the C-level libpq library, most strings passed to the library must be C-compatible. This means they must end with a single null (0) byte and cannot contain internal null bytes.

    The risk with text strings

    If you pass a string containing a zero byte as a parameter (e.g., a prepared statement name or a parameter value), the library will treat the string as ending at the first zero byte encountered. The resulting value will be truncated to everything before that byte.

    Handling binary data

    If you need to pass data containing zero bytes, you must treat it as binary data (SQL BYTEA type) rather than a text string. In libpqxx, represent binary data using contiguous memory containing std::byte. Supported types include:

    • pqxx::bytes
    • pqxx::bytes_view
    • std::vector<std::byte>
    • std::array<std::byte, ...>
    • std::span<std::byte>
  2. Handle Result and Row iterator lifetime changes

    master

    In libpqxx 8.0, result and row iterators no longer reference-count their parent result object.

    Crucial: You must ensure the result object remains in scope for as long as you are using its iterators. Unlike in 7.x, the iterator will not keep the container alive. However, result objects themselves are still reference-counted smart pointers to the underlying data, making copies of the result object relatively cheap.

  3. Debug with Source Locations

    master

    Most libpqxx API functions now accept an optional std::source_location argument (abbreviated as pqxx::sl).

    When an exception is thrown, the error message will attempt to include this source location to help you identify exactly where the error occurred in your application code. By default, the location reported is the boundary where your code called into libpqxx.

  4. Understand row_ref and field_ref vs row and field

    master

    libpqxx uses different classes to represent rows and fields depending on whether you want efficiency or data ownership:

    ClassTypeBehavior
    row_refPointer-likeA pointer to the result plus a row number. Very efficient.
    rowCopy-likeA copy of the result plus a row number. Keeps the underlying data alive.
    field_refPointer-likeA pointer to the result with row and column numbers. Very efficient.
    fieldCopy-likeA copy of the result with row and column numbers. Keeps the underlying data alive.

    Best Practice: Use row_ref and field_ref for most operations. They are cheap to copy and pass by value. Only use row or field if you need to keep the data alive after the original result object has been destroyed.

  5. How to handle NULL values in streams

    master

    When streaming data, libpqxx performs data conversion between C++ and SQL types. To represent a SQL NULL for C++ types that do not have a built-in null value (like int), wrap the type in std::optional.

    Supported wrappers for handling nulls include:

    • std::optional<T> (preferred for efficiency)
    • std::unique_ptr<T>
    • std::shared_ptr<T>

    For example, using std::optional<int> allows the stream to correctly map a SQL NULL to a state where the integer has no value, whereas a raw int would always require a value.

  6. How type conversions work in libpqxx

    master

    libpqxx performs conversions between C++ types and PostgreSQL's text format. Conversions are driven by the C++ type you specify, not the SQL type in the database.

    • To String: Use pqxx::to_string(value) or pqxx::to_buf(buf, value, ctx) to convert a C++ value to its SQL text representation.
    • From String: Use pqxx::from_string<T>(text, ctx) to parse an SQL text value into a C++ type T.

    If a conversion is invalid (e.g., reading a negative SQL integer into a C++ unsigned int), libpqxx throws a pqxx::conversion_error.

  7. Handle encodings for complex parameters

    master

    When using complex types (like composite types), pqxx::params may require knowledge of the text encoding. To provide this, pass a pqxx::encoding_group as the first argument to your parameter collection.

    Note: This special first argument is used only for encoding information and will not be treated as a parameter in your SQL statement.

    As a shortcut, you can pass a reference to your pqxx::connection, pqxx::transaction, or pqxx::conversion_context instead of explicitly providing an encoding group.

  8. Understand the core libpqxx workflow

    master

    The libpqxx library is built around three primary types that work in a specific lifecycle:

    1. pqxx::connection: Establishes the connection to the database. The constructor accepts connection strings compatible with libpq's PQconnectdb.
    2. pqxx::transaction (typically pqxx::work): An object created from a connection to manage a unit of work. You execute SQL via this object. Crucially, you must call .commit() to make changes permanent; otherwise, the work is automatically rolled back when the transaction object is destroyed.
    3. pqxx::result: The container returned by execution functions. It holds multiple pqxx::row objects, which in turn contain pqxx::field objects.

    Once a transaction is committed or destroyed, the connection is available for a new transaction.

        pqxx::connection cx;
        pqxx::work tx(cx);
        pqxx::row r = tx.exec("SELECT 1").one_row();
        tx.commit();
        std::cout << r[0].as<int>() << std::endl;
  9. Understand thread safety in libpqxx

    master

    libpqxx does not include internal locking to protect objects from simultaneous modification. It is the responsibility of the developer to prevent conflicting operations in multi-threaded programs.

    Core Mental Model: The "World" Concept

    Treat a pqxx::connection and all objects derived from it (transactions, cursors, etc.) as a single, isolated "world". To avoid race conditions, ensure that a single "world" is never accessed by multiple threads simultaneously if any thread is performing a non-const (modifying) operation.

    Safe and Unsafe Operations

    • Safe: Result sets are immutable and can be shared between threads without synchronization.
    • Unsafe: Performing non-const operations on a shared "world". Examples include:
      • Issuing a query on a transaction while simultaneously opening a subtransaction.
      • Accessing a cursor while another thread is committing the transaction.
      • Any non-const operation on a cursor (cursors are particularly sensitive and require conservative locking if shared).

    Verifying Thread Safety at Runtime

    You can check the specific thread safety guarantees of your current libpqxx build and version using pqxx::describe_thread_safety(). This returns a pqxx::thread_safety_model object that describes the supported concurrency model.

  10. How libpqxx performs configuration tests

    master

    libpqxx uses configuration tests to verify that the compiler environment supports specific C++ features (for example, checking if std::to_chars for floating-point types is available).

    These tests are integrated into both the GNU autotools and CMake build systems. The process works by attempting to compile specific C++ code snippets (prefixed with PQXX_*.cxx) during the configuration phase. If the compilation succeeds, the feature is marked as supported.