mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-21 05:53:09 +00:00
be7ce4110e
This reverts commit e96515433d
.
1262 lines
46 KiB
C++
1262 lines
46 KiB
C++
/* 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-2022, 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 <functional>
|
|
#include <initializer_list>
|
|
#include <list>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string_view>
|
|
#include <tuple>
|
|
#include <utility>
|
|
|
|
// 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"
|
|
|
|
|
|
/**
|
|
* @addtogroup connections
|
|
*
|
|
* Use of the libpqxx library starts here.
|
|
*
|
|
* Everything that can be done with a database through libpqxx must go through
|
|
* a @ref pqxx::connection object. It connects to a database when you create
|
|
* it, and it terminates that communication during destruction.
|
|
*
|
|
* Many things come together in this class. Handling of error and warning
|
|
* messages, for example, is defined by @ref pqxx::errorhandler objects in the
|
|
* context of a connection. Prepared statements are also defined here.
|
|
*
|
|
* When you connect to a database, you pass a connection string containing any
|
|
* parameters and options, such as the server address and the database name.
|
|
*
|
|
* These are identical to the ones in libpq, the C language binding upon which
|
|
* libpqxx itself is built:
|
|
*
|
|
* https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
|
|
*
|
|
* There are also environment variables you can set to provide defaults, again
|
|
* as defined by libpq:
|
|
*
|
|
* https://www.postgresql.org/docs/current/libpq-envars.html
|
|
*
|
|
* You can also create a database connection _asynchronously_ using an
|
|
* intermediate @ref pqxx::connecting object.
|
|
*/
|
|
|
|
namespace pqxx::internal
|
|
{
|
|
class sql_cursor;
|
|
|
|
#if defined(PQXX_HAVE_CONCEPTS)
|
|
/// Concept: T is a range of pairs of zero-terminated strings.
|
|
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
|
|
} // 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;
|
|
class connection_stream_from;
|
|
class connection_stream_to;
|
|
class connection_transaction;
|
|
class const_connection_largeobject;
|
|
} // namespace pqxx::internal::gate
|
|
|
|
|
|
namespace pqxx
|
|
{
|
|
/// Representation of a PostgreSQL table path.
|
|
/** A "table path" consists of a table name, optionally prefixed by a schema
|
|
* name, which in turn is optionally prefixed by a database name.
|
|
*
|
|
* A minimal example of a table path would be `{mytable}`. But a table path
|
|
* may also take the forms `{myschema,mytable}` or
|
|
* `{mydb,myschema,mytable}`.
|
|
*/
|
|
using table_path = std::initializer_list<std::string_view>;
|
|
|
|
|
|
/// Encrypt a password. @deprecated Use connection::encrypt_password instead.
|
|
[[nodiscard,
|
|
deprecated("Use connection::encrypt_password instead.")]] std::string
|
|
PQXX_LIBEXPORT
|
|
encrypt_password(char const user[], char const password[]);
|
|
|
|
/// Encrypt password. @deprecated Use connection::encrypt_password instead.
|
|
[[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"
|
|
}
|
|
|
|
|
|
/// Error verbosity levels.
|
|
enum class error_verbosity : int
|
|
{
|
|
// These values must match those in libpq's PGVerbosity enum.
|
|
terse = 0,
|
|
normal = 1,
|
|
verbose = 2
|
|
};
|
|
|
|
|
|
/// Connection to a database.
|
|
/** This is the first class to look at when you wish to work with a database
|
|
* through libpqxx. The connection opens during construction, and closes upon
|
|
* destruction.
|
|
*
|
|
* When creating a connection, you can pass a connection URI or a postgres
|
|
* connection string, to specify the database server's address, a login
|
|
* username, and so on. If you don't, the connection will try to obtain them
|
|
* from certain environment variables. If those are not set either, the
|
|
* default is to try and connect to the local system's port 5432.
|
|
*
|
|
* Find more about connection strings here:
|
|
*
|
|
* https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
|
|
*
|
|
* The variables are documented here:
|
|
*
|
|
* https://www.postgresql.org/docs/current/libpq-envars.html
|
|
*
|
|
* To query or manipulate the database once connected, use one of the
|
|
* transaction classes (see pqxx/transaction_base.hxx) and perhaps also the
|
|
* transactor framework (see pqxx/transactor.hxx).
|
|
*
|
|
* When a connection breaks, you will typically get a @ref broken_connection
|
|
* exception. This can happen at almost any point.
|
|
*
|
|
* @warning On Unix-like systems, including GNU and BSD systems, your program
|
|
* may receive the SIGPIPE signal when the connection to the backend breaks. By
|
|
* default this signal will abort your program. Use "signal(SIGPIPE, SIG_IGN)"
|
|
* if you want your program to continue running after a connection fails.
|
|
*/
|
|
class PQXX_LIBEXPORT connection
|
|
{
|
|
public:
|
|
connection() : connection{""} {}
|
|
|
|
/// Connect to a database, using `options` string.
|
|
explicit connection(char const options[])
|
|
{
|
|
check_version();
|
|
init(options);
|
|
}
|
|
|
|
/// Connect to a database, using `options` string.
|
|
explicit connection(zview options) : connection{options.c_str()}
|
|
{
|
|
// (Delegates to other constructor which calls check_version for us.)
|
|
}
|
|
|
|
/// Move constructor.
|
|
/** Moving a connection is not allowed if it has an open transaction, or has
|
|
* error handlers or notification receivers registered on it. In those
|
|
* situations, other objects may hold references to the old object which
|
|
* would become invalid and might produce hard-to-diagnose bugs.
|
|
*/
|
|
connection(connection &&rhs);
|
|
|
|
#if defined(PQXX_HAVE_CONCEPTS)
|
|
/// Connect to a database, passing options as a range of key/value pairs.
|
|
/** @warning Experimental. Requires C++20 "concepts" support. Define
|
|
* `PQXX_HAVE_CONCEPTS` to enable it.
|
|
*
|
|
* There's no need to escape the parameter values.
|
|
*
|
|
* See the PostgreSQL libpq documentation for the full list of possible
|
|
* options:
|
|
*
|
|
* https://postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
|
|
*
|
|
* The options can be anything that can be iterated as a series of pairs of
|
|
* zero-terminated strings: `std::pair<std::string, std::string>`, or
|
|
* `std::tuple<pqxx::zview, char const *>`, or
|
|
* `std::map<std::string, pqxx::zview>`, and so on.
|
|
*/
|
|
template<internal::ZKey_ZValues MAPPING>
|
|
inline connection(MAPPING const ¶ms);
|
|
#endif // PQXX_HAVE_CONCEPTS
|
|
|
|
~connection()
|
|
{
|
|
try
|
|
{
|
|
close();
|
|
}
|
|
catch (std::exception const &)
|
|
{}
|
|
}
|
|
|
|
/// Move assignment.
|
|
/** Neither connection can have an open transaction, registered error
|
|
* handlers, or registered notification receivers.
|
|
*/
|
|
connection &operator=(connection &&rhs);
|
|
|
|
connection(connection const &) = delete;
|
|
connection &operator=(connection const &) = delete;
|
|
|
|
/// Is this connection open at the moment?
|
|
/** @warning This function is **not** needed in most code. Resist the
|
|
* temptation to check it after opening a connection. The `connection`
|
|
* constructor will throw a @ref broken_connection exception if can't connect
|
|
* to the database.
|
|
*/
|
|
[[nodiscard]] bool PQXX_PURE is_open() const noexcept;
|
|
|
|
/// Invoke notice processor function. The message should end in newline.
|
|
void process_notice(char const[]) noexcept;
|
|
/// Invoke notice processor function. Newline at end is recommended.
|
|
/** The zview variant, with a message ending in newline, is the most
|
|
* efficient way to call process_notice.
|
|
*/
|
|
void process_notice(zview) noexcept;
|
|
|
|
/// Enable tracing to a given output stream, or nullptr to disable.
|
|
void trace(std::FILE *) noexcept;
|
|
|
|
/**
|
|
* @name Connection properties
|
|
*
|
|
* These are probably not of great interest, since most are derived from
|
|
* information supplied by the client program itself, but they are included
|
|
* for completeness.
|
|
*
|
|
* The connection needs to be currently active for these to work.
|
|
*/
|
|
//@{
|
|
/// Name of database we're connected to, if any.
|
|
[[nodiscard]] char const *dbname() const;
|
|
|
|
/// Database user ID we're connected under, if any.
|
|
[[nodiscard]] char const *username() const;
|
|
|
|
/// Address of server, or nullptr if none specified (i.e. default or local)
|
|
[[nodiscard]] char const *hostname() const;
|
|
|
|
/// Server port number we're connected to.
|
|
[[nodiscard]] char const *port() const;
|
|
|
|
/// Process ID for backend process, or 0 if inactive.
|
|
[[nodiscard]] int PQXX_PURE backendpid() const &noexcept;
|
|
|
|
/// Socket currently used for connection, or -1 for none. Use with care!
|
|
/** Query the current socket number. This is intended for event loops based
|
|
* on functions such as select() or poll(), where you're waiting for any of
|
|
* multiple file descriptors to become ready for communication.
|
|
*
|
|
* Please try to stay away from this function. It is really only meant for
|
|
* event loops that need to wait on more than one file descriptor. If all
|
|
* you need is to block until a notification arrives, for instance, use
|
|
* await_notification(). If you want to issue queries and retrieve results
|
|
* in nonblocking fashion, check out the pipeline class.
|
|
*/
|
|
[[nodiscard]] int PQXX_PURE sock() const &noexcept;
|
|
|
|
/// What version of the PostgreSQL protocol is this connection using?
|
|
/** The answer can be 0 (when there is no connection); 3 for protocol 3.0; or
|
|
* possibly higher values as newer protocol versions come into use.
|
|
*/
|
|
[[nodiscard]] int PQXX_PURE protocol_version() const noexcept;
|
|
|
|
/// What version of the PostgreSQL server are we connected to?
|
|
/** The result is a bit complicated: each of the major, medium, and minor
|
|
* release numbers is written as a two-digit decimal number, and the three
|
|
* are then concatenated. Thus server version 9.4.2 will be returned as the
|
|
* decimal number 90402. If there is no connection to the server, this
|
|
* returns zero.
|
|
*
|
|
* @warning When writing version numbers in your code, don't add zero at the
|
|
* beginning! Numbers beginning with zero are interpreted as octal (base-8)
|
|
* in C++. Thus, 070402 is not the same as 70402, and 080000 is not a number
|
|
* at all because there is no digit "8" in octal notation. Use strictly
|
|
* decimal notation when it comes to these version numbers.
|
|
*/
|
|
[[nodiscard]] int PQXX_PURE server_version() const noexcept;
|
|
//@}
|
|
|
|
/// @name Text encoding
|
|
/**
|
|
* Each connection is governed by a "client encoding," which dictates how
|
|
* strings and other text is represented in bytes. The database server will
|
|
* send text data to you in this encoding, and you should use it for the
|
|
* queries and data which you send to the server.
|
|
*
|
|
* Search the PostgreSQL documentation for "character set encodings" to find
|
|
* out more about the available encodings, how to extend them, and how to use
|
|
* them. Not all server-side encodings are compatible with all client-side
|
|
* encodings or vice versa.
|
|
*
|
|
* Encoding names are case-insensitive, so e.g. "UTF8" is equivalent to
|
|
* "utf8".
|
|
*
|
|
* You can change the client encoding, but this may not work when the
|
|
* connection is in a special state, such as when streaming a table. It's
|
|
* not clear what happens if you change the encoding during a transaction,
|
|
* and then abort the transaction.
|
|
*/
|
|
//@{
|
|
/// Get client-side character encoding, by name.
|
|
[[nodiscard]] std::string get_client_encoding() const;
|
|
|
|
/// Set client-side character encoding, by name.
|
|
/**
|
|
* @param encoding Name of the character set encoding to use.
|
|
*/
|
|
void set_client_encoding(zview encoding) &
|
|
{
|
|
set_client_encoding(encoding.c_str());
|
|
}
|
|
|
|
/// Set client-side character encoding, by name.
|
|
/**
|
|
* @param encoding Name of the character set encoding to use.
|
|
*/
|
|
void set_client_encoding(char const encoding[]) &;
|
|
|
|
/// Get the connection's encoding, as a PostgreSQL-defined code.
|
|
[[nodiscard]] int PQXX_PRIVATE encoding_id() const;
|
|
|
|
//@}
|
|
|
|
/// Set session variable, using SQL's `SET` command.
|
|
/** @deprecated To set a session variable, use @ref set_session_var. To set
|
|
* a transaction-local variable, execute an SQL `SET` command.
|
|
*
|
|
* @warning When setting a string value, you must escape and quote it first.
|
|
* Use the @ref quote() function to do that.
|
|
*
|
|
* @warning This executes an SQL query, so do not get or set variables while
|
|
* a table stream or pipeline is active on the same connection.
|
|
*
|
|
* @param var Variable to set.
|
|
* @param value New value for Var. This can be any SQL expression. If it's
|
|
* a string, be sure that it's properly escaped and quoted.
|
|
*/
|
|
[[deprecated("To set session variables, use set_session_var.")]] void
|
|
set_variable(std::string_view var, std::string_view value) &;
|
|
|
|
/// Set one of the session variables to a new value.
|
|
/** This executes SQL, so do not do it while a pipeline or stream is active
|
|
* on the connection.
|
|
*
|
|
* The value you set here will last for the rest of the connection's
|
|
* duration, or until you set a new value.
|
|
*
|
|
* If you set the value while in a @ref dbtransaction (i.e. any transaction
|
|
* that is not a @ref nontransaction), then rolling back the transaction will
|
|
* undo the change.
|
|
*
|
|
* All applies to setting _session_ variables. You can also set the same
|
|
* variables as _local_ variables, in which case they will always revert to
|
|
* their previous value when the transaction ends (or when you overwrite them
|
|
* of course). To set a local variable, simply execute an SQL statement
|
|
* along the lines of "`SET LOCAL var = 'value'`" inside your transaction.
|
|
*
|
|
* @param var The variable to set.
|
|
* @param value The new value for the variable.
|
|
* @throw @ref variable_set_to_null if the value is null; this is not
|
|
* allowed.
|
|
*/
|
|
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)));
|
|
}
|
|
|
|
/// Read session variable, using SQL's `SHOW` command.
|
|
/** @warning This executes an SQL query, so do not get or set variables while
|
|
* a table stream or pipeline is active on the same connection.
|
|
*/
|
|
[[deprecated("Use get_var instead.")]] std::string
|
|
get_variable(std::string_view);
|
|
|
|
/// Read currently applicable value of a variable.
|
|
/** This function executes an SQL statement, so it won't work while a
|
|
* @ref pipeline or query stream is active on the connection.
|
|
*
|
|
* @return a blank `std::optional` if the variable's value is null, or its
|
|
* string value otherwise.
|
|
*/
|
|
std::string get_var(std::string_view var);
|
|
|
|
/// Read currently applicable value of a variable.
|
|
/** This function executes an SQL statement, so it won't work while a
|
|
* @ref pipeline or query stream is active on the connection.
|
|
*
|
|
* If there is any possibility that the variable is null, ensure that `TYPE`
|
|
* can represent null values.
|
|
*/
|
|
template<typename TYPE> TYPE get_var_as(std::string_view var)
|
|
{
|
|
return from_string<TYPE>(get_var(var));
|
|
}
|
|
|
|
/**
|
|
* @name Notifications and Receivers
|
|
*/
|
|
//@{
|
|
/// Check for pending notifications and take appropriate action.
|
|
/** This does not block. To wait for incoming notifications, either call
|
|
* await_notification() (it calls this function); or wait for incoming data
|
|
* on the connection's socket (i.e. wait to read), and then call this
|
|
* function repeatedly until it returns zero. After that, there are no more
|
|
* pending notifications so you may want to wait again.
|
|
*
|
|
* If any notifications are pending when you call this function, it
|
|
* processes them by finding any receivers that match the notification string
|
|
* and invoking those. If no receivers match, there is nothing to invoke but
|
|
* we do consider the notification processed.
|
|
*
|
|
* If any of the client-registered receivers throws an exception, the
|
|
* function will report it using the connection's errorhandlers. It does not
|
|
* re-throw the exceptions.
|
|
*
|
|
* @return Number of notifications processed.
|
|
*/
|
|
int get_notifs();
|
|
|
|
/// Wait for a notification to come in.
|
|
/** There are other events that will also terminate the wait, such as the
|
|
* backend failing. It will also wake up periodically.
|
|
*
|
|
* If a notification comes in, the call will process it, along with any other
|
|
* notifications that may have been pending.
|
|
*
|
|
* To wait for notifications into your own event loop instead, wait until
|
|
* there is incoming data on the connection's socket to be read, then call
|
|
* @ref get_notifs() repeatedly until it returns zero.
|
|
*
|
|
* @return Number of notifications processed.
|
|
*/
|
|
int await_notification();
|
|
|
|
/// Wait for a notification to come in, or for given timeout to pass.
|
|
/** There are other events that will also terminate the wait, such as the
|
|
* backend failing, or timeout expiring.
|
|
*
|
|
* If a notification comes in, the call will process it, along with any other
|
|
* notifications that may have been pending.
|
|
*
|
|
* To wait for notifications into your own event loop instead, wait until
|
|
* there is incoming data on the connection's socket to be read, then call
|
|
* @ref get_notifs repeatedly until it returns zero.
|
|
*
|
|
* @return Number of notifications processed
|
|
*/
|
|
int await_notification(std::time_t seconds, long microseconds);
|
|
//@}
|
|
|
|
/**
|
|
* @name Password encryption
|
|
*
|
|
* Use this when setting a new password for the user if password encryption
|
|
* is enabled. Inputs are the SQL name for the user for whom you with to
|
|
* encrypt a password; the plaintext password; and the hash algorithm.
|
|
*
|
|
* The algorithm must be one of "md5", "scram-sha-256" (introduced in
|
|
* PostgreSQL 10), or `nullptr`. If the pointer is null, this will query
|
|
* the `password_encryption setting` from the server, and use the default
|
|
* algorithm as defined there.
|
|
*
|
|
* @return encrypted version of the password, suitable for encrypted
|
|
* PostgreSQL authentication.
|
|
*
|
|
* Thus you can change a user's password with:
|
|
* ```cxx
|
|
* void setpw(transaction_base &t, string const &user, string const &pw)
|
|
* {
|
|
* t.exec0("ALTER USER " + user + " "
|
|
* "PASSWORD '" + t.conn().encrypt_password(user,pw) + "'");
|
|
* }
|
|
* ```
|
|
*
|
|
* When building this against a libpq older than version 10, this will use
|
|
* an older function which only supports md5. In that case, requesting a
|
|
* different algorithm than md5 will result in a @ref feature_not_supported
|
|
* exception.
|
|
*/
|
|
//@{
|
|
/// Encrypt a password for a given user.
|
|
[[nodiscard]] std::string
|
|
encrypt_password(zview user, zview password, zview algorithm)
|
|
{
|
|
return encrypt_password(user.c_str(), password.c_str(), algorithm.c_str());
|
|
}
|
|
/// Encrypt a password for a given user.
|
|
[[nodiscard]] std::string encrypt_password(
|
|
char const user[], char const password[], char const *algorithm = nullptr);
|
|
//@}
|
|
|
|
/**
|
|
* @name Prepared statements
|
|
*
|
|
* PostgreSQL supports prepared SQL statements, i.e. statements that you can
|
|
* register under a name you choose, optimized once by the backend, and
|
|
* executed any number of times under the given name.
|
|
*
|
|
* Prepared statement definitions are not sensitive to transaction
|
|
* boundaries. A statement defined inside a transaction will remain defined
|
|
* outside that transaction, even if the transaction itself is subsequently
|
|
* aborted. Once a statement has been prepared, it will only go away if you
|
|
* close the connection or explicitly "unprepare" the statement.
|
|
*
|
|
* Use the `pqxx::transaction_base::exec_prepared` functions to execute a
|
|
* prepared statement. See @ref prepared for a full discussion.
|
|
*
|
|
* @warning Using prepared statements can save time, but if your statement
|
|
* takes parameters, it may also make your application significantly slower!
|
|
* The reason is that the server works out a plan for executing the query
|
|
* when you prepare it. At that time, of course it does not know the values
|
|
* for the parameters that you will pass. If you execute a query without
|
|
* preparing it, then the server works out the plan on the spot, with full
|
|
* knowledge of the parameter values.
|
|
*
|
|
* A statement's definition can refer to its parameters as `$1`, `$2`, etc.
|
|
* The first parameter you pass to the call provides a value for `$1`, and
|
|
* so on.
|
|
*
|
|
* Here's an example of how to use prepared statements.
|
|
*
|
|
* ```cxx
|
|
* using namespace pqxx;
|
|
* void foo(connection &c)
|
|
* {
|
|
* c.prepare("findtable", "select * from pg_tables where name=$1");
|
|
* work tx{c};
|
|
* result r = tx.exec_prepared("findtable", "mytable");
|
|
* if (std::empty(r)) throw runtime_error{"mytable not found!"};
|
|
* }
|
|
* ```
|
|
*/
|
|
//@{
|
|
|
|
/// Define a prepared statement.
|
|
/**
|
|
* @param name unique name for the new prepared statement.
|
|
* @param definition SQL statement to prepare.
|
|
*/
|
|
void prepare(zview name, zview definition) &
|
|
{
|
|
prepare(name.c_str(), definition.c_str());
|
|
}
|
|
|
|
/**
|
|
* @param name unique name for the new prepared statement.
|
|
* @param definition SQL statement to prepare.
|
|
*/
|
|
void prepare(char const name[], char const definition[]) &;
|
|
|
|
/// Define a nameless prepared statement.
|
|
/**
|
|
* This can be useful if you merely want to pass large binary parameters to a
|
|
* statement without otherwise wishing to prepare it. If you use this
|
|
* feature, always keep the definition and the use close together to avoid
|
|
* the nameless statement being redefined unexpectedly by code somewhere
|
|
* else.
|
|
*/
|
|
void prepare(char const definition[]) &;
|
|
void prepare(zview definition) & { return prepare(definition.c_str()); }
|
|
|
|
/// Drop prepared statement.
|
|
void unprepare(std::string_view name);
|
|
|
|
//@}
|
|
|
|
// C++20: constexpr. Breaks ABI.
|
|
/// Suffix unique number to name to make it unique within session context.
|
|
/** Used internally to generate identifiers for SQL objects (such as cursors
|
|
* and nested transactions) based on a given human-readable base name.
|
|
*/
|
|
[[nodiscard]] std::string adorn_name(std::string_view);
|
|
|
|
/**
|
|
* @defgroup escaping-functions String-escaping functions
|
|
*/
|
|
//@{
|
|
|
|
/// Escape string for use as SQL string literal on this connection.
|
|
/** @warning This accepts a length, and it does not require a terminating
|
|
* zero byte. But if there is a zero byte, escaping stops there even if
|
|
* it's not at the end of the string!
|
|
*/
|
|
[[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});
|
|
}
|
|
|
|
/// Escape string for use as SQL string literal on this connection.
|
|
[[nodiscard]] std::string esc(char const text[]) const
|
|
{
|
|
return esc(std::string_view{text});
|
|
}
|
|
|
|
#if defined(PQXX_HAVE_SPAN)
|
|
/// Escape string for use as SQL string literal, into `buffer`.
|
|
/** Use this variant when you want to re-use the same buffer across multiple
|
|
* calls. If that's not the case, or convenience and simplicity are more
|
|
* important, use the single-argument variant.
|
|
*
|
|
* For every byte in `text`, there must be at least 2 bytes of space in
|
|
* `buffer`; plus there must be one byte of space for a trailing zero.
|
|
* Throws @ref range_error if this space is not available.
|
|
*
|
|
* Returns a reference to the escaped string, which is actually stored in
|
|
* `buffer`.
|
|
*/
|
|
[[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
|
|
|
|
/// Escape string for use as SQL string literal on this connection.
|
|
/** @warning This is meant for text strings only. It cannot contain bytes
|
|
* whose value is zero ("nul bytes").
|
|
*/
|
|
[[nodiscard]] std::string esc(std::string_view text) const;
|
|
|
|
#if defined(PQXX_HAVE_CONCEPTS)
|
|
/// Escape binary string for use as SQL string literal on this connection.
|
|
/** This is identical to `esc_raw(data)`. */
|
|
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)
|
|
/// Escape binary string for use as SQL string literal, into `buffer`.
|
|
/** Use this variant when you want to re-use the same buffer across multiple
|
|
* calls. If that's not the case, or convenience and simplicity are more
|
|
* important, use the single-argument variant.
|
|
*
|
|
* For every byte in `data`, there must be at least two bytes of space in
|
|
* `buffer`; plus there must be two bytes of space for a header and one for
|
|
* a trailing zero. Throws @ref range_error if this space is not available.
|
|
*
|
|
* Returns a reference to the escaped string, which is actually stored in
|
|
* `buffer`.
|
|
*/
|
|
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
|
|
|
|
/// Escape binary string for use as SQL string literal on this connection.
|
|
[[deprecated("Use std::byte for binary data.")]] std::string
|
|
esc_raw(unsigned char const bin[], std::size_t len) const;
|
|
|
|
/// Escape binary string for use as SQL string literal on this connection.
|
|
/** You can also just use @ref esc with a binary string. */
|
|
[[nodiscard]] std::string esc_raw(std::basic_string_view<std::byte>) const;
|
|
|
|
#if defined(PQXX_HAVE_SPAN)
|
|
/// Escape binary string for use as SQL string literal, into `buffer`.
|
|
/** You can also just use @ref esc with a binary string. */
|
|
[[nodiscard]] std::string
|
|
esc_raw(std::basic_string_view<std::byte>, std::span<char> buffer) const;
|
|
#endif
|
|
|
|
#if defined(PQXX_HAVE_CONCEPTS)
|
|
/// Escape binary string for use as SQL string literal on this connection.
|
|
/** You can also just use @ref esc with a binary string. */
|
|
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)
|
|
/// Escape binary string for use as SQL string literal, into `buffer`.
|
|
template<binary DATA>
|
|
[[nodiscard]] zview esc_raw(DATA const &data, std::span<char> buffer) const
|
|
{
|
|
return this->esc(binary_cast(data), buffer);
|
|
}
|
|
#endif
|
|
|
|
/// Unescape binary data, e.g. from a table field or notification payload.
|
|
/** Takes a binary string as escaped by PostgreSQL, and returns a restored
|
|
* copy of the original binary data.
|
|
*/
|
|
[[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"
|
|
}
|
|
|
|
/// Unescape binary data, e.g. from a table field or notification payload.
|
|
/** Takes a binary string as escaped by PostgreSQL, and returns a restored
|
|
* copy of the original binary data.
|
|
*/
|
|
[[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string
|
|
unesc_raw(char const text[]) const;
|
|
|
|
// TODO: Make "into buffer" variant to eliminate a string allocation.
|
|
/// Unescape binary data, e.g. from a table field or notification payload.
|
|
/** Takes a binary string as escaped by PostgreSQL, and returns a restored
|
|
* copy of the original binary data.
|
|
*
|
|
* (The data must be encoded in PostgreSQL's "hex" format. The legacy
|
|
* "bytea" escape format, used prior to PostgreSQL 9.0, is no longer
|
|
* supported.)
|
|
*/
|
|
[[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;
|
|
}
|
|
|
|
/// Escape and quote a string of binary data.
|
|
[[deprecated("Use quote(std::basic_string_view<std::byte>).")]] std::string
|
|
quote_raw(unsigned char const bin[], std::size_t len) const;
|
|
|
|
/// Escape and quote a string of binary data.
|
|
std::string quote_raw(std::basic_string_view<std::byte>) const;
|
|
|
|
#if defined(PQXX_HAVE_CONCEPTS)
|
|
/// Escape and quote a string of binary data.
|
|
/** You can also just use @ref quote with binary data. */
|
|
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.
|
|
/// Escape and quote an SQL identifier for use in a query.
|
|
[[nodiscard]] std::string quote_name(std::string_view identifier) const;
|
|
|
|
// TODO: Make "into buffer" variant to eliminate a string allocation.
|
|
/// Escape and quote a table name.
|
|
/** When passing just a table name, this is just another name for
|
|
* @ref quote_name.
|
|
*/
|
|
[[nodiscard]] std::string quote_table(std::string_view name) const;
|
|
|
|
// TODO: Make "into buffer" variant to eliminate a string allocation.
|
|
/// Escape and quote a table path.
|
|
/** A table path consists of a table name, optionally prefixed by a schema
|
|
* name; and if both are given, they are in turn optionally prefixed by a
|
|
* database name.
|
|
*
|
|
* Each portion of the path (database name, schema name, table name) will be
|
|
* quoted separately, and they will be joined together by dots. So for
|
|
* example, `myschema.mytable` will become `"myschema"."mytable"`.
|
|
*/
|
|
[[nodiscard]] std::string quote_table(table_path) const;
|
|
|
|
// TODO: Make "into buffer" variant to eliminate a string allocation.
|
|
/// Quote and comma-separate a series of column names.
|
|
/** Use this to save a bit of work in cases where you repeatedly need to pass
|
|
* the same list of column names, e.g. with @ref stream_to and @ref
|
|
* stream_from. Some functions that need to quote the columns list
|
|
* internally, will have a "raw" alternative which let you do the quoting
|
|
* yourself. It's a bit of extra work, but it can in rare cases let you
|
|
* eliminate some duplicate work in quoting them repeatedly.
|
|
*/
|
|
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.
|
|
/// Represent object as SQL string, including quoting & escaping.
|
|
/**
|
|
* Recognises nulls and represents them as SQL nulls. They get no quotes.
|
|
*/
|
|
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.
|
|
/// Escape and quote binary data for use as a BYTEA value in SQL statement.
|
|
[[nodiscard]] std::string
|
|
quote(std::basic_string_view<std::byte> bytes) const;
|
|
|
|
// TODO: Make "into buffer" variant to eliminate a string allocation.
|
|
/// Escape string for literal LIKE match.
|
|
/** Use this when part of an SQL "LIKE" pattern should match only as a
|
|
* literal string, not as a pattern, even if it contains "%" or "_"
|
|
* characters that would normally act as wildcards.
|
|
*
|
|
* The string does not get string-escaped or quoted. You do that later.
|
|
*
|
|
* For instance, let's say you have a string `name` entered by the user,
|
|
* and you're searching a `file` column for items that match `name`
|
|
* followed by a dot and three letters. Even if `name` contains wildcard
|
|
* characters "%" or "_", you only want those to match literally, so "_"
|
|
* only matches "_" and "%" only matches a single "%".
|
|
*
|
|
* You do that by "like-escaping" `name`, appending the wildcard pattern
|
|
* `".___"`, and finally, escaping and quoting the result for inclusion in
|
|
* your query:
|
|
*
|
|
* ```cxx
|
|
* tx.exec(
|
|
* "SELECT file FROM item WHERE file LIKE " +
|
|
* tx.quote(tx.esc_like(name) + ".___"));
|
|
* ```
|
|
*
|
|
* The SQL "LIKE" operator also lets you choose your own escape character.
|
|
* This is supported, but must be a single-byte character.
|
|
*/
|
|
[[nodiscard]] std::string
|
|
esc_like(std::string_view text, char escape_char = '\\') const;
|
|
//@}
|
|
|
|
/// Attempt to cancel the ongoing query, if any.
|
|
/** You can use this from another thread, and/or while a query is executing
|
|
* in a pipeline, but it's up to you to ensure that you're not canceling the
|
|
* wrong query. This may involve locking.
|
|
*/
|
|
void cancel_query();
|
|
|
|
#if defined(_WIN32) || __has_include(<fcntl.h>)
|
|
/// Set socket to blocking (true) or nonblocking (false).
|
|
/** @warning Do not use this unless you _really_ know what you're doing.
|
|
* @warning This function is available on most systems, but not necessarily
|
|
* all.
|
|
*/
|
|
void set_blocking(bool block) &;
|
|
#endif // defined(_WIN32) || __has_include(<fcntl.h>)
|
|
|
|
/// Set session verbosity.
|
|
/** Set the verbosity of error messages to "terse", "normal" (the default),
|
|
* or "verbose."
|
|
*
|
|
* If "terse", returned messages include severity, primary text, and
|
|
* position only; this will normally fit on a single line. "normal" produces
|
|
* messages that include the above plus any detail, hint, or context fields
|
|
* (these might span multiple lines). "verbose" includes all available
|
|
* fields.
|
|
*/
|
|
void set_verbosity(error_verbosity verbosity) &noexcept;
|
|
|
|
/// Return pointers to the active errorhandlers.
|
|
/** The entries are ordered from oldest to newest handler.
|
|
*
|
|
* You may use this to find errorhandlers that your application wants to
|
|
* delete when destroying the connection. Be aware, however, that libpqxx
|
|
* may also add errorhandlers of its own, and those will be included in the
|
|
* list. If this is a problem for you, derive your errorhandlers from a
|
|
* custom base class derived from pqxx::errorhandler. Then use dynamic_cast
|
|
* to find which of the error handlers are yours.
|
|
*
|
|
* The pointers point to the real errorhandlers. The container it returns
|
|
* however is a copy of the one internal to the connection, not a reference.
|
|
*/
|
|
[[nodiscard]] std::vector<errorhandler *> get_errorhandlers() const;
|
|
|
|
/// Return a connection string encapsulating this connection's options.
|
|
/** The connection must be currently open for this to work.
|
|
*
|
|
* Returns a reconstruction of this connection's connection string. It may
|
|
* not exactly match the connection string you passed in when creating this
|
|
* connection.
|
|
*/
|
|
[[nodiscard]] std::string connection_string() const;
|
|
|
|
/// Explicitly close the connection.
|
|
/** The destructor will do this for you automatically. Still, there is a
|
|
* reason to `close()` objects explicitly where possible: if an error should
|
|
* occur while closing, `close()` can throw an exception. A destructor
|
|
* cannot.
|
|
*
|
|
* Closing a connection is idempotent. Closing a connection that's already
|
|
* closed does nothing.
|
|
*/
|
|
void close();
|
|
|
|
/// Seize control of a raw libpq connection.
|
|
/** @warning Do not do this. Please. It's for very rare, very specific
|
|
* use-cases. The mechanism may change (or break) in unexpected ways in
|
|
* future versions.
|
|
*
|
|
* @param raw_conn a raw libpq `PQconn` pointer.
|
|
*/
|
|
static connection seize_raw_connection(internal::pq::PGconn *raw_conn)
|
|
{
|
|
return connection{raw_conn};
|
|
}
|
|
|
|
/// Release the raw connection without closing it.
|
|
/** @warning Do not do this. It's for very rare, very specific use-cases.
|
|
* The mechanism may change (or break) in unexpected ways in future versions.
|
|
*
|
|
* The `connection` object becomes unusable after this.
|
|
*/
|
|
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);
|
|
|
|
/// For use by @ref seize_raw_connection.
|
|
explicit connection(internal::pq::PGconn *raw_conn) : m_conn{raw_conn} {}
|
|
|
|
/// Poll for ongoing connection, try to progress towards completion.
|
|
/** Returns a pair of "now please wait to read data from socket" and "now
|
|
* please wait to write data to socket." Both will be false when done.
|
|
*
|
|
* Throws an exception if polling indicates that the connection has failed.
|
|
*/
|
|
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;
|
|
|
|
/// Escape a string, into a buffer allocated by the caller.
|
|
/** The buffer must have room for at least `2*std::size(text) + 1` bytes.
|
|
*
|
|
* Returns the number of bytes written, including the trailing zero.
|
|
*/
|
|
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 &);
|
|
|
|
/// Throw @ref usage_error if this connection is not in a movable state.
|
|
void check_movable() const;
|
|
/// Throw @ref usage_error if not in a state where it can be move-assigned.
|
|
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 class internal::gate::connection_stream_from;
|
|
std::pair<std::unique_ptr<char, std::function<void(char *)>>, std::size_t>
|
|
PQXX_PRIVATE 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);
|
|
|
|
/// Connection handle.
|
|
internal::pq::PGconn *m_conn = nullptr;
|
|
|
|
/// Active transaction on connection, if any.
|
|
/** We don't use this for anything, except to check for open transactions
|
|
* when we close the connection or start a new transaction.
|
|
*
|
|
* We also don't allow move construction or move assignment while there's a
|
|
* transaction, since moving the connection in that case would leave one or
|
|
* more pointers back from the transaction to the connection dangling.
|
|
*/
|
|
transaction_base const *m_trans = nullptr;
|
|
|
|
std::list<errorhandler *> m_errorhandlers;
|
|
|
|
using receiver_list =
|
|
std::multimap<std::string, pqxx::notification_receiver *>;
|
|
/// Notification receivers.
|
|
receiver_list m_receivers;
|
|
|
|
/// Unique number to use as suffix for identifiers (see adorn_name()).
|
|
int m_unique_id = 0;
|
|
};
|
|
|
|
|
|
/// @deprecated Old base class for connection. They are now the same class.
|
|
using connection_base = connection;
|
|
|
|
|
|
/// An ongoing, non-blocking stepping stone to a connection.
|
|
/** Use this when you want to create a connection to the database, but without
|
|
* blocking your whole thread. It is only available on systems that have
|
|
* the `<fcntl.h>` header, and Windows.
|
|
*
|
|
* Connecting in this way is probably not "faster" (it's more complicated and
|
|
* has some extra overhead), but in some situations you can use it to make your
|
|
* application as a whole faster. It all depends on having other useful work
|
|
* to do in the same thread, and being able to wait on a socket. If you have
|
|
* other I/O going on at the same time, your event loop can wait for both the
|
|
* libpqxx socket and your own sockets, and wake up whenever any of them is
|
|
* ready to do work.
|
|
*
|
|
* Connecting in this way is not properly "asynchronous;" it's merely
|
|
* "nonblocking." This means it's not a super-high-performance mechanism like
|
|
* you might get with e.g. `io_uring`. In particular, if we need to look up
|
|
* the database hostname in DNS, that will happen synchronously.
|
|
*
|
|
* To use this, create the `connecting` object, passing a connection string.
|
|
* Then loop: If @ref wait_to_read returns true, wait for the socket to have
|
|
* incoming data on it. If @ref wait_to_write returns true, wait for the
|
|
* socket to be ready for writing. Then call @ref process to process any
|
|
* incoming or outgoing data. Do all of this until @ref done returns true (or
|
|
* there is an exception). Finally, call @ref produce to get the completed
|
|
* connection.
|
|
*
|
|
* For example:
|
|
*
|
|
* ```cxx
|
|
* pqxx::connecting cg{};
|
|
*
|
|
* // Loop until we're done connecting.
|
|
* while (!cg.done())
|
|
* {
|
|
* wait_for_fd(cg.sock(), cg.wait_to_read(), cg.wait_to_write());
|
|
* cg.process();
|
|
* }
|
|
*
|
|
* pqxx::connection conn = std::move(cg).produce();
|
|
*
|
|
* // At this point, conn is a working connection. You can no longer use
|
|
* // cg at all.
|
|
* ```
|
|
*/
|
|
class PQXX_LIBEXPORT connecting
|
|
{
|
|
public:
|
|
/// Start connecting.
|
|
connecting(zview connection_string = ""_zv);
|
|
|
|
connecting(connecting const &) = delete;
|
|
connecting(connecting &&) = default;
|
|
connecting &operator=(connecting const &) = delete;
|
|
connecting &operator=(connecting &&) = default;
|
|
|
|
/// Get the socket. The socket may change during the connection process.
|
|
[[nodiscard]] int sock() const &noexcept { return m_conn.sock(); }
|
|
|
|
/// Should we currently wait to be able to _read_ from the socket?
|
|
[[nodiscard]] constexpr bool wait_to_read() const &noexcept
|
|
{
|
|
return m_reading;
|
|
}
|
|
|
|
/// Should we currently wait to be able to _write_ to the socket?
|
|
[[nodiscard]] constexpr bool wait_to_write() const &noexcept
|
|
{
|
|
return m_writing;
|
|
}
|
|
|
|
/// Progress towards completion (but don't block).
|
|
void process() &;
|
|
|
|
/// Is our connection finished?
|
|
[[nodiscard]] constexpr bool done() const &noexcept
|
|
{
|
|
return not m_reading and not m_writing;
|
|
}
|
|
|
|
/// Produce the completed connection object.
|
|
/** Use this only once, after @ref done returned `true`. Once you have
|
|
* called this, the `connecting` instance has no more use or meaning. You
|
|
* can't call any of its member functions afterwards.
|
|
*
|
|
* This member function is rvalue-qualified, meaning that you can only call
|
|
* it on an rvalue instance of the class. If what you have is not an rvalue,
|
|
* turn it into one by wrapping it in `std::move()`.
|
|
*/
|
|
[[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 ¶ms)
|
|
{
|
|
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
|