connection.hxx
/* Definition of the connection class.
*
* pqxx::connection encapsulates a connection to a database.
*
* DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/connection instead.
*
* Copyright (c) 2000-2023, 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.
*/
#ifndef PQXX_H_CONNECTION
#define PQXX_H_CONNECTION
#if !defined(PQXX_HEADER_PRE)
# error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
#endif
#include <cstddef>
#include <ctime>
#include <initializer_list>
#include <list>
#include <map>
#include <memory>
#include <string_view>
#include <tuple>
// Double-check in order to suppress an overzealous Visual C++ warning (#418).
#if defined(PQXX_HAVE_CONCEPTS) && __has_include(<ranges>)
# include <ranges>
#endif
#include "pqxx/errorhandler.hxx"
#include "pqxx/except.hxx"
#include "pqxx/internal/concat.hxx"
#include "pqxx/params.hxx"
#include "pqxx/separated_list.hxx"
#include "pqxx/strconv.hxx"
#include "pqxx/types.hxx"
#include "pqxx/util.hxx"
#include "pqxx/zview.hxx"
namespace pqxx::internal
{
class sql_cursor;
#if defined(PQXX_HAVE_CONCEPTS)
template<typename T>
concept ZKey_ZValues =
std::ranges::input_range<T> and
requires(T t) {
{
std::cbegin(t)
};
{
std::get<0>(*std::cbegin(t))
} -> ZString;
{
std::get<1>(*std::cbegin(t))
} -> ZString;
} and
std::tuple_size_v<typename std::ranges::iterator_t<T>::value_type> == 2;
#endif // PQXX_HAVE_CONCEPTS
void PQXX_COLD PQXX_LIBEXPORT skip_init_ssl(int flags) noexcept;
} // namespace pqxx::internal
namespace pqxx::internal::gate
{
class connection_dbtransaction;
class connection_errorhandler;
class connection_largeobject;
class connection_notification_receiver;
class connection_pipeline;
class connection_sql_cursor;
struct connection_stream_from;
class connection_stream_to;
class connection_transaction;
class const_connection_largeobject;
} // namespace pqxx::internal::gate
namespace pqxx
{
enum skip_init : int
{
nothing,
openssl,
crypto,
};
template<skip_init... SKIP> inline void skip_init_ssl() noexcept
{
// (Normalise skip flags to one per.)
pqxx::internal::skip_init_ssl(((1 << SKIP) | ...));
}
using table_path = std::initializer_list<std::string_view>;
[[nodiscard,
deprecated("Use connection::encrypt_password instead.")]] std::string
PQXX_LIBEXPORT
encrypt_password(char const user[], char const password[]);
[[nodiscard,
deprecated("Use connection::encrypt_password instead.")]] inline std::string
encrypt_password(zview user, zview password)
{
#include "pqxx/internal/ignore-deprecated-pre.hxx"
return encrypt_password(user.c_str(), password.c_str());
#include "pqxx/internal/ignore-deprecated-post.hxx"
}
enum class error_verbosity : int
{
// These values must match those in libpq's PGVerbosity enum.
terse = 0,
normal = 1,
verbose = 2
};
class PQXX_LIBEXPORT connection
{
public:
connection() : connection{""} {}
explicit connection(char const options[])
{
check_version();
init(options);
}
explicit connection(zview options) : connection{options.c_str()}
{
// (Delegates to other constructor which calls check_version for us.)
}
connection(connection &&rhs);
#if defined(PQXX_HAVE_CONCEPTS)
template<internal::ZKey_ZValues MAPPING>
inline connection(MAPPING const &params);
#endif // PQXX_HAVE_CONCEPTS
~connection()
{
try
{
close();
}
catch (std::exception const &)
{}
}
connection &operator=(connection &&rhs);
connection(connection const &) = delete;
connection &operator=(connection const &) = delete;
[[nodiscard]] bool PQXX_PURE is_open() const noexcept;
void process_notice(char const[]) noexcept;
void process_notice(zview) noexcept;
void trace(std::FILE *) noexcept;
[[nodiscard]] char const *dbname() const;
[[nodiscard]] char const *username() const;
[[nodiscard]] char const *hostname() const;
[[nodiscard]] char const *port() const;
[[nodiscard]] int PQXX_PURE backendpid() const &noexcept;
[[nodiscard]] int PQXX_PURE sock() const &noexcept;
[[nodiscard]] int PQXX_PURE protocol_version() const noexcept;
[[nodiscard]] int PQXX_PURE server_version() const noexcept;
[[nodiscard]] std::string get_client_encoding() const;
void set_client_encoding(zview encoding) &
{
set_client_encoding(encoding.c_str());
}
void set_client_encoding(char const encoding[]) &;
[[nodiscard]] int encoding_id() const;
[[deprecated("To set session variables, use set_session_var.")]] void
set_variable(std::string_view var, std::string_view value) &;
template<typename TYPE>
void set_session_var(std::string_view var, TYPE const &value) &
{
if constexpr (nullness<TYPE>::has_null)
{
if (nullness<TYPE>::is_null(value))
throw variable_set_to_null{
internal::concat("Attempted to set variable ", var, " to null.")};
}
exec(internal::concat("SET ", quote_name(var), "=", quote(value)));
}
[[deprecated("Use get_var instead.")]] std::string
get_variable(std::string_view);
std::string get_var(std::string_view var);
template<typename TYPE> TYPE get_var_as(std::string_view var)
{
return from_string<TYPE>(get_var(var));
}
int get_notifs();
int await_notification();
int await_notification(std::time_t seconds, long microseconds);
[[nodiscard]] std::string
encrypt_password(zview user, zview password, zview algorithm)
{
return encrypt_password(user.c_str(), password.c_str(), algorithm.c_str());
}
[[nodiscard]] std::string encrypt_password(
char const user[], char const password[], char const *algorithm = nullptr);
void prepare(zview name, zview definition) &
{
prepare(name.c_str(), definition.c_str());
}
void prepare(char const name[], char const definition[]) &;
void prepare(char const definition[]) &;
void prepare(zview definition) & { return prepare(definition.c_str()); }
void unprepare(std::string_view name);
// C++20: constexpr. Breaks ABI.
[[nodiscard]] std::string adorn_name(std::string_view);
[[deprecated("Use std::string_view or pqxx:zview.")]] std::string
esc(char const text[], std::size_t maxlen) const
{
return esc(std::string_view{text, maxlen});
}
[[nodiscard]] std::string esc(char const text[]) const
{
return esc(std::string_view{text});
}
#if defined(PQXX_HAVE_SPAN)
[[nodiscard]] std::string_view
esc(std::string_view text, std::span<char> buffer)
{
auto const size{std::size(text)}, space{std::size(buffer)};
auto const needed{2 * size + 1};
if (space < needed)
throw range_error{internal::concat(
"Not enough room to escape string of ", size, " byte(s): need ",
needed, " bytes of buffer space, but buffer size is ", space, ".")};
auto const data{buffer.data()};
return {data, esc_to_buf(text, data)};
}
#endif
[[nodiscard]] std::string esc(std::string_view text) const;
#if defined(PQXX_HAVE_CONCEPTS)
template<binary DATA> [[nodiscard]] std::string esc(DATA const &data) const
{
return esc_raw(data);
}
#endif
#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN)
template<binary DATA>
[[nodiscard]] zview esc(DATA const &data, std::span<char> buffer) const
{
auto const size{std::size(data)}, space{std::size(buffer)};
auto const needed{internal::size_esc_bin(std::size(data))};
if (space < needed)
throw range_error{internal::concat(
"Not enough room to escape binary string of ", size, " byte(s): need ",
needed, " bytes of buffer space, but buffer size is ", space, ".")};
std::basic_string_view<std::byte> view{std::data(data), std::size(data)};
auto const out{std::data(buffer)};
// Actually, in the modern format, we know beforehand exactly how many
// bytes we're going to fill. Just leave out the trailing zero.
internal::esc_bin(view, out);
return zview{out, needed - 1};
}
#endif
[[deprecated("Use std::byte for binary data.")]] std::string
esc_raw(unsigned char const bin[], std::size_t len) const;
[[nodiscard]] std::string esc_raw(std::basic_string_view<std::byte>) const;
#if defined(PQXX_HAVE_SPAN)
[[nodiscard]] std::string
esc_raw(std::basic_string_view<std::byte>, std::span<char> buffer) const;
#endif
#if defined(PQXX_HAVE_CONCEPTS)
template<binary DATA>
[[nodiscard]] std::string esc_raw(DATA const &data) const
{
return esc_raw(
std::basic_string_view<std::byte>{std::data(data), std::size(data)});
}
#endif
#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN)
template<binary DATA>
[[nodiscard]] zview esc_raw(DATA const &data, std::span<char> buffer) const
{
return this->esc(binary_cast(data), buffer);
}
#endif
[[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string
unesc_raw(zview text) const
{
#include "pqxx/internal/ignore-deprecated-pre.hxx"
return unesc_raw(text.c_str());
#include "pqxx/internal/ignore-deprecated-post.hxx"
}
[[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string
unesc_raw(char const text[]) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
[[nodiscard]] std::basic_string<std::byte>
unesc_bin(std::string_view text) const
{
std::basic_string<std::byte> buf;
buf.resize(pqxx::internal::size_unesc_bin(std::size(text)));
pqxx::internal::unesc_bin(text, buf.data());
return buf;
}
[[deprecated("Use quote(std::basic_string_view<std::byte>).")]] std::string
quote_raw(unsigned char const bin[], std::size_t len) const;
std::string quote_raw(std::basic_string_view<std::byte>) const;
#if defined(PQXX_HAVE_CONCEPTS)
template<binary DATA>
[[nodiscard]] std::string quote_raw(DATA const &data) const
{
return quote_raw(
std::basic_string_view<std::byte>{std::data(data), std::size(data)});
}
#endif
// TODO: Make "into buffer" variant to eliminate a string allocation.
[[nodiscard]] std::string quote_name(std::string_view identifier) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
[[nodiscard]] std::string quote_table(std::string_view name) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
[[nodiscard]] std::string quote_table(table_path) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
template<PQXX_CHAR_STRINGS_ARG STRINGS>
inline std::string quote_columns(STRINGS const &columns) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
template<typename T>
[[nodiscard]] inline std::string quote(T const &t) const;
[[deprecated("Use std::byte for binary data.")]] std::string
quote(binarystring const &) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
[[nodiscard]] std::string
quote(std::basic_string_view<std::byte> bytes) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
[[nodiscard]] std::string
esc_like(std::string_view text, char escape_char = '\\') const;
void cancel_query();
#if defined(_WIN32) || __has_include(<fcntl.h>)
void set_blocking(bool block) &;
#endif // defined(_WIN32) || __has_include(<fcntl.h>)
void set_verbosity(error_verbosity verbosity) &noexcept;
[[nodiscard]] std::vector<errorhandler *> get_errorhandlers() const;
[[nodiscard]] std::string connection_string() const;
void close();
static connection seize_raw_connection(internal::pq::PGconn *raw_conn)
{
return connection{raw_conn};
}
internal::pq::PGconn *release_raw_connection() &&
{
return std::exchange(m_conn, nullptr);
}
private:
friend class connecting;
enum connect_mode
{
connect_nonblocking
};
connection(connect_mode, zview connection_string);
explicit connection(internal::pq::PGconn *raw_conn) : m_conn{raw_conn} {}
std::pair<bool, bool> poll_connect();
// Initialise based on connection string.
void init(char const options[]);
// Initialise based on parameter names and values.
void init(char const *params[], char const *values[]);
void complete_init();
result make_result(
internal::pq::PGresult *pgr, std::shared_ptr<std::string> const &query,
std::string_view desc = ""sv);
void PQXX_PRIVATE set_up_state();
int PQXX_PRIVATE PQXX_PURE status() const noexcept;
std::size_t esc_to_buf(std::string_view text, char *buf) const;
friend class internal::gate::const_connection_largeobject;
char const *PQXX_PURE err_msg() const noexcept;
void PQXX_PRIVATE process_notice_raw(char const msg[]) noexcept;
result exec_prepared(std::string_view statement, internal::c_params const &);
void check_movable() const;
void check_overwritable() const;
friend class internal::gate::connection_errorhandler;
void PQXX_PRIVATE register_errorhandler(errorhandler *);
void PQXX_PRIVATE unregister_errorhandler(errorhandler *) noexcept;
friend class internal::gate::connection_transaction;
result exec(std::string_view, std::string_view = ""sv);
result
PQXX_PRIVATE exec(std::shared_ptr<std::string>, std::string_view = ""sv);
void PQXX_PRIVATE register_transaction(transaction_base *);
void PQXX_PRIVATE unregister_transaction(transaction_base *) noexcept;
friend struct internal::gate::connection_stream_from;
std::pair<std::unique_ptr<char, void (*)(void const *)>, std::size_t>
read_copy_line();
friend class internal::gate::connection_stream_to;
void PQXX_PRIVATE write_copy_line(std::string_view);
void PQXX_PRIVATE end_copy_write();
friend class internal::gate::connection_largeobject;
internal::pq::PGconn *raw_connection() const { return m_conn; }
friend class internal::gate::connection_notification_receiver;
void add_receiver(notification_receiver *);
void remove_receiver(notification_receiver *) noexcept;
friend class internal::gate::connection_pipeline;
void PQXX_PRIVATE start_exec(char const query[]);
bool PQXX_PRIVATE consume_input() noexcept;
bool PQXX_PRIVATE is_busy() const noexcept;
internal::pq::PGresult *get_result();
friend class internal::gate::connection_dbtransaction;
friend class internal::gate::connection_sql_cursor;
result exec_params(std::string_view query, internal::c_params const &args);
internal::pq::PGconn *m_conn = nullptr;
transaction_base const *m_trans = nullptr;
std::list<errorhandler *> m_errorhandlers;
using receiver_list =
std::multimap<std::string, pqxx::notification_receiver *>;
receiver_list m_receivers;
int m_unique_id = 0;
};
using connection_base = connection;
class PQXX_LIBEXPORT connecting
{
public:
connecting(zview connection_string = ""_zv);
connecting(connecting const &) = delete;
connecting(connecting &&) = default;
connecting &operator=(connecting const &) = delete;
connecting &operator=(connecting &&) = default;
[[nodiscard]] int sock() const &noexcept { return m_conn.sock(); }
[[nodiscard]] constexpr bool wait_to_read() const &noexcept
{
return m_reading;
}
[[nodiscard]] constexpr bool wait_to_write() const &noexcept
{
return m_writing;
}
void process() &;
[[nodiscard]] constexpr bool done() const &noexcept
{
return not m_reading and not m_writing;
}
[[nodiscard]] connection produce() &&;
private:
connection m_conn;
bool m_reading{false};
bool m_writing{true};
};
template<typename T> inline std::string connection::quote(T const &t) const
{
if constexpr (nullness<T>::always_null)
{
return "NULL";
}
else
{
if (is_null(t))
return "NULL";
auto const text{to_string(t)};
// Okay, there's an easy way to do this and there's a hard way. The easy
// way was "quote, esc(to_string(t)), quote". I'm going with the hard way
// because it's going to save some string manipulation that will probably
// incur some unnecessary memory allocations and deallocations.
std::string buf{'\''};
buf.resize(2 + 2 * std::size(text) + 1);
auto const content_bytes{esc_to_buf(text, buf.data() + 1)};
auto const closing_quote{1 + content_bytes};
buf[closing_quote] = '\'';
auto const end{closing_quote + 1};
buf.resize(end);
return buf;
}
}
template<PQXX_CHAR_STRINGS_ARG STRINGS>
inline std::string connection::quote_columns(STRINGS const &columns) const
{
return separated_list(
","sv, std::cbegin(columns), std::cend(columns),
[this](auto col) { return this->quote_name(*col); });
}
#if defined(PQXX_HAVE_CONCEPTS)
template<internal::ZKey_ZValues MAPPING>
inline connection::connection(MAPPING const &params)
{
check_version();
std::vector<char const *> keys, values;
if constexpr (std::ranges::sized_range<MAPPING>)
{
auto const size{std::ranges::size(params) + 1};
keys.reserve(size);
values.reserve(size);
}
for (auto const &[key, value] : params)
{
keys.push_back(internal::as_c_string(key));
values.push_back(internal::as_c_string(value));
}
keys.push_back(nullptr);
values.push_back(nullptr);
init(std::data(keys), std::data(values));
}
#endif // PQXX_HAVE_CONCEPTS
} // namespace pqxx
#endif
pqxx
The home of all libpqxx classes, functions, templates, etc.
Definition array.hxx:33
pqxx::separated_list
std::string separated_list(std::string_view sep, ITER begin, ITER end, ACCESS access)
Represent sequence of values as a string, joined by a given separator.
Definition separated_list.hxx:44
pqxx::encrypt_password
std::string encrypt_password(char const user[], char const password[])
Encrypt a password.
Definition connection.cxx:94
pqxx::binary_cast
std::basic_string_view< std::byte > binary_cast(TYPE const &data)
Cast binary data to a type that libpqxx will recognise as binary.
Definition util.hxx:303
pqxx::skip_init_ssl
void skip_init_ssl() noexcept
Control initialisation of OpenSSL and libcrypto libraries.
Definition connection.hxx:173
pqxx::table_path
std::initializer_list< std::string_view > table_path
Representation of a PostgreSQL table path.
Definition connection.hxx:188
pqxx::check_version
void check_version() noexcept
Definition util.hxx:234
pqxx::to_string
std::string to_string(field const &value)
Convert a field to a string.
Definition result.cxx:549
pqxx::skip_init
skip_init
Flags for skipping initialisation of SSL-related libraries.
Definition connection.hxx:137
pqxx::crypto
@ crypto
Skip initialisation of libcrypto.
Definition connection.hxx:145
pqxx::openssl
@ openssl
Skip initialisation of OpenSSL library.
Definition connection.hxx:142
pqxx::nothing
@ nothing
A do-nothing flag that does not affect anything.
Definition connection.hxx:139
pqxx::error_verbosity
error_verbosity
Error verbosity levels.
Definition connection.hxx:210
pqxx::error_verbosity::terse
@ terse
pqxx::error_verbosity::verbose
@ verbose
pqxx::error_verbosity::normal
@ normal
pqxx::format::text
@ text
pqxx::internal
Internal items for libpqxx' own use. Do not use these yourself.
Definition composite.hxx:84
pqxx::internal::skip_init_ssl
void PQXX_COLD skip_init_ssl(int flags) noexcept
Control OpenSSL/crypto library initialisation.
Definition connection.cxx:83
pqxx::internal::unesc_bin
void unesc_bin(std::string_view escaped_data, std::byte buffer[])
Reconstitute binary data from its escaped version.
Definition util.cxx:158
pqxx::internal::esc_bin
void esc_bin(std::basic_string_view< std::byte > binary_data, char buffer[]) noexcept
Hex-escape binary data into a buffer.
Definition util.cxx:126
pqxx::internal::size_esc_bin
constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
Compute buffer size needed to escape binary data for use as a BYTEA.
Definition util.hxx:411
pqxx::internal::as_c_string
constexpr char const * as_c_string(char const str[]) noexcept
Get a raw C string pointer.
Definition zview.hxx:145
pqxx::internal::size_unesc_bin
constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
Compute binary size from the size of its escaped version.
Definition util.hxx:420
pqxx::internal::gate
Definition connection.hxx:112
pqxx::connection
Connection to a database.
Definition connection.hxx:253
pqxx::connection::encrypt_password
std::string encrypt_password(zview user, zview password, zview algorithm)
Encrypt a password for a given user.
Definition connection.hxx:612
pqxx::connection::prepare
void prepare(zview definition) &
Definition connection.hxx:689
pqxx::connection::connection
connection(char const options[])
Connect to a database, using options string.
Definition connection.hxx:258
pqxx::connection::operator=
connection & operator=(connection const &)=delete
pqxx::connection::get_var_as
TYPE get_var_as(std::string_view var)
Read currently applicable value of a variable.
Definition connection.hxx:520
pqxx::connection::connection
connection()
Definition connection.hxx:255
pqxx::connection::~connection
~connection()
Definition connection.hxx:299
pqxx::connection::quote_columns
std::string quote_columns(STRINGS const &columns) const
Quote and comma-separate a series of column names.
Definition connection.hxx:1301
pqxx::connection::connection
connection(zview options)
Connect to a database, using options string.
Definition connection.hxx:265
pqxx::connection::release_raw_connection
internal::pq::PGconn * release_raw_connection() &&
Release the raw connection without closing it.
Definition connection.hxx:1052
pqxx::connection::seize_raw_connection
static connection seize_raw_connection(internal::pq::PGconn *raw_conn)
Seize control of a raw libpq connection.
Definition connection.hxx:1041
pqxx::connection::unesc_bin
std::basic_string< std::byte > unesc_bin(std::string_view text) const
Unescape binary data, e.g. from a table field or notification payload.
Definition connection.hxx:863
pqxx::connection::esc
std::string esc(char const text[]) const
Escape string for use as SQL string literal on this connection.
Definition connection.hxx:720
pqxx::connection::unesc_raw
std::string unesc_raw(zview text) const
Unescape binary data, e.g. from a table field or notification payload.
Definition connection.hxx:839
pqxx::connection::esc
std::string esc(char const text[], std::size_t maxlen) const
Escape string for use as SQL string literal on this connection.
Definition connection.hxx:714
pqxx::connection::prepare
void prepare(zview name, zview definition) &
Define a prepared statement.
Definition connection.hxx:669
pqxx::connection::set_session_var
void set_session_var(std::string_view var, TYPE const &value) &
Set one of the session variables to a new value.
Definition connection.hxx:486
pqxx::connection::quote
std::string quote(T const &t) const
Represent object as SQL string, including quoting & escaping.
Definition connection.hxx:1272
pqxx::connection::connection
connection(connection const &)=delete
pqxx::connecting
An ongoing, non-blocking stepping stone to a connection.
Definition connection.hxx:1220
pqxx::connecting::connecting
connecting(connecting &&)=default
pqxx::connecting::sock
int sock() const &noexcept
Get the socket. The socket may change during the connection process.
Definition connection.hxx:1231
pqxx::connecting::done
constexpr bool done() const &noexcept
Is our connection finished?
Definition connection.hxx:1249
pqxx::connecting::operator=
connecting & operator=(connecting const &)=delete
pqxx::connecting::connecting
connecting(connecting const &)=delete
pqxx::connecting::wait_to_write
constexpr bool wait_to_write() const &noexcept
Should we currently wait to be able to write to the socket?
Definition connection.hxx:1240
pqxx::connecting::process
void process() &
Progress towards completion (but don't block).
pqxx::connecting::operator=
connecting & operator=(connecting &&)=default
pqxx::connecting::wait_to_read
constexpr bool wait_to_read() const &noexcept
Should we currently wait to be able to read from the socket?
Definition connection.hxx:1234
pqxx::connecting::connecting
connecting(zview connection_string=""_zv)
Start connecting.
pqxx::connecting::produce
connection produce() &&
Produce the completed connection object.
pqxx::errorhandler
Base class for error-handler callbacks.
Definition errorhandler.hxx:54
pqxx::variable_set_to_null
The caller attempted to set a variable to null, which is not allowed.
Definition except.hxx:116
pqxx::notification_receiver
Definition notification.hxx:57
pqxx::params
Build a parameter list for a parameterised or prepared statement.
Definition params.hxx:220
pqxx::result
Result set containing data returned by a query or command.
Definition result.hxx:73
pqxx::nullness
Traits describing a type's "null value," if any.
Definition strconv.hxx:93
pqxx::transaction_base
Interface definition (and common code) for "transaction" classes.
Definition transaction_base.hxx:88
pqxx::zview
Marker-type wrapper: zero-terminated std::string_view.
Definition zview.hxx:38
pqxx::zview::c_str
constexpr char const * c_str() const &noexcept
Either a null pointer, or a zero-terminated text buffer.
Definition zview.hxx:96