mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-30 09:48:54 +00:00
be7ce4110e
This reverts commit e96515433d
.
811 lines
30 KiB
C++
811 lines
30 KiB
C++
/* Common code and definitions for the transaction classes.
|
|
*
|
|
* pqxx::transaction_base defines the interface for any abstract class that
|
|
* represents a database transaction.
|
|
*
|
|
* DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/transaction_base 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_TRANSACTION_BASE
|
|
#define PQXX_H_TRANSACTION_BASE
|
|
|
|
#if !defined(PQXX_HEADER_PRE)
|
|
# error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
|
|
#endif
|
|
|
|
#include <string_view>
|
|
#include <utility>
|
|
|
|
/* End-user programs need not include this file, unless they define their own
|
|
* transaction classes. This is not something the typical program should want
|
|
* to do.
|
|
*
|
|
* However, reading this file is worthwhile because it defines the public
|
|
* interface for the available transaction classes such as transaction and
|
|
* nontransaction.
|
|
*/
|
|
|
|
#include "pqxx/connection.hxx"
|
|
#include "pqxx/internal/concat.hxx"
|
|
#include "pqxx/internal/encoding_group.hxx"
|
|
#include "pqxx/isolation.hxx"
|
|
#include "pqxx/result.hxx"
|
|
#include "pqxx/row.hxx"
|
|
#include "pqxx/stream_from.hxx"
|
|
#include "pqxx/util.hxx"
|
|
|
|
namespace pqxx::internal::gate
|
|
{
|
|
class transaction_subtransaction;
|
|
class transaction_sql_cursor;
|
|
class transaction_stream_to;
|
|
class transaction_transaction_focus;
|
|
} // namespace pqxx::internal::gate
|
|
|
|
|
|
namespace pqxx
|
|
{
|
|
using namespace std::literals;
|
|
|
|
|
|
class transaction_focus;
|
|
|
|
|
|
/**
|
|
* @defgroup transactions Transaction classes
|
|
*
|
|
* All database access goes through instances of these classes.
|
|
* However, not all implementations of this interface need to provide full
|
|
* transactional integrity.
|
|
*
|
|
* Several implementations of this interface are shipped with libpqxx,
|
|
* including the plain transaction class, the entirely unprotected
|
|
* nontransaction, and the more cautious robusttransaction.
|
|
*/
|
|
|
|
/// Interface definition (and common code) for "transaction" classes.
|
|
/**
|
|
* @ingroup transactions
|
|
*
|
|
* Abstract base class for all transaction types.
|
|
*/
|
|
class PQXX_LIBEXPORT PQXX_NOVTABLE transaction_base
|
|
{
|
|
public:
|
|
transaction_base() = delete;
|
|
transaction_base(transaction_base const &) = delete;
|
|
transaction_base(transaction_base &&) = delete;
|
|
transaction_base &operator=(transaction_base const &) = delete;
|
|
transaction_base &operator=(transaction_base &&) = delete;
|
|
|
|
virtual ~transaction_base() = 0;
|
|
|
|
/// Commit the transaction.
|
|
/** Make the effects of this transaction definite. If you destroy a
|
|
* transaction without invoking its @ref commit() first, that will implicitly
|
|
* abort it. (For the @ref nontransaction class though, "commit" and "abort"
|
|
* really don't do anything, hence its name.)
|
|
*
|
|
* There is, however, a minute risk that you might lose your connection to
|
|
* the database at just the wrong moment here. In that case, libpqxx may be
|
|
* unable to determine whether the database was able to complete the
|
|
* transaction, or had to roll it back. In that scenario, @ref commit() will
|
|
* throw an in_doubt_error. There is a different transaction class called
|
|
* @ref robusttransaction which takes some special precautions to reduce this
|
|
* risk.
|
|
*/
|
|
void commit();
|
|
|
|
/// Abort the transaction.
|
|
/** No special effort is required to call this function; it will be called
|
|
* implicitly when the transaction is destructed.
|
|
*/
|
|
void abort();
|
|
|
|
/**
|
|
* @ingroup escaping-functions
|
|
*
|
|
* Use these when writing SQL queries that incorporate C++ values as SQL
|
|
* constants.
|
|
*
|
|
* The functions you see here are just convenience shortcuts to the same
|
|
* functions on the connection object.
|
|
*/
|
|
//@{
|
|
/// Escape string for use as SQL string literal in this transaction.
|
|
template<typename... ARGS> [[nodiscard]] auto esc(ARGS &&...args) const
|
|
{
|
|
return conn().esc(std::forward<ARGS>(args)...);
|
|
}
|
|
|
|
/// Escape binary data for use as SQL string literal in this transaction.
|
|
/** Raw, binary data is treated differently from regular strings. Binary
|
|
* strings are never interpreted as text, so they may safely include byte
|
|
* values or byte sequences that don't happen to represent valid characters
|
|
* in the character encoding being used.
|
|
*
|
|
* The binary string does not stop at the first zero byte, as is the case
|
|
* with textual strings. Instead, it may contain zero bytes anywhere. If
|
|
* it happens to contain bytes that look like quote characters, or other
|
|
* things that can disrupt their use in SQL queries, they will be replaced
|
|
* with special escape sequences.
|
|
*/
|
|
template<typename... ARGS> [[nodiscard]] auto esc_raw(ARGS &&...args) const
|
|
{
|
|
return conn().esc_raw(std::forward<ARGS>(args)...);
|
|
}
|
|
|
|
/// 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 conn().unesc_raw(text);
|
|
#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]] std::basic_string<std::byte> unesc_bin(zview text)
|
|
{
|
|
return conn().unesc_bin(text);
|
|
}
|
|
|
|
/// 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
|
|
{
|
|
#include "pqxx/internal/ignore-deprecated-pre.hxx"
|
|
return conn().unesc_raw(text);
|
|
#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]] std::basic_string<std::byte> unesc_bin(char const text[])
|
|
{
|
|
return conn().unesc_bin(text);
|
|
}
|
|
|
|
/// Represent object as SQL string, including quoting & escaping.
|
|
/** Nulls are recognized and represented as SQL nulls. */
|
|
template<typename T> [[nodiscard]] std::string quote(T const &t) const
|
|
{
|
|
return conn().quote(t);
|
|
}
|
|
|
|
[[deprecated(
|
|
"Use std::basic_string<std::byte> instead of binarystring.")]] std::string
|
|
quote(binarystring const &t) const
|
|
{
|
|
return conn().quote(t.bytes_view());
|
|
}
|
|
|
|
/// Binary-escape and quote a binary string for use as an SQL constant.
|
|
[[deprecated("Use quote(std::basic_string_view<std::byte>).")]] std::string
|
|
quote_raw(unsigned char const bin[], std::size_t len) const
|
|
{
|
|
return quote(binary_cast(bin, len));
|
|
}
|
|
|
|
/// Binary-escape and quote a binary string for use as an SQL constant.
|
|
[[deprecated("Use quote(std::basic_string_view<std::byte>).")]] std::string
|
|
quote_raw(zview bin) const;
|
|
|
|
#if defined(PQXX_HAVE_CONCEPTS)
|
|
/// Binary-escape and quote a binary string for use as an SQL constant.
|
|
/** For binary data you can also just use @ref quote(data). */
|
|
template<binary DATA>
|
|
[[nodiscard]] std::string quote_raw(DATA const &data) const
|
|
{
|
|
return conn().quote_raw(data);
|
|
}
|
|
#endif
|
|
|
|
/// Escape an SQL identifier for use in a query.
|
|
[[nodiscard]] std::string quote_name(std::string_view identifier) const
|
|
{
|
|
return conn().quote_name(identifier);
|
|
}
|
|
|
|
/// Escape string for literal LIKE match.
|
|
[[nodiscard]] std::string
|
|
esc_like(std::string_view bin, char escape_char = '\\') const
|
|
{
|
|
return conn().esc_like(bin, escape_char);
|
|
}
|
|
//@}
|
|
|
|
/**
|
|
* @name Command execution
|
|
*
|
|
* There are many functions for executing (or "performing") a command (or
|
|
* "query"). This is the most fundamental thing you can do with the library,
|
|
* and you always do it from a transaction class.
|
|
*
|
|
* Command execution can throw many types of exception, including sql_error,
|
|
* broken_connection, and many sql_error subtypes such as
|
|
* feature_not_supported or insufficient_privilege. But any exception thrown
|
|
* by the C++ standard library may also occur here. All exceptions you will
|
|
* see libpqxx throw are derived from std::exception.
|
|
*
|
|
* One unusual feature in libpqxx is that you can give your query a name or
|
|
* description. This does not mean anything to the database, but sometimes
|
|
* it can help libpqxx produce more helpful error messages, making problems
|
|
* in your code easier to debug.
|
|
*
|
|
* Many of the execution functions used to accept a `desc` argument, a
|
|
* human-readable description of the statement for use in error messages.
|
|
* This could make failures easier to debug. Future versions will use
|
|
* C++20's `std::source_location` to identify the failing statement.
|
|
*/
|
|
//@{
|
|
|
|
/// Execute a command.
|
|
/**
|
|
* @param query Query or command to execute.
|
|
* @param desc Optional identifier for query, to help pinpoint SQL errors.
|
|
* @return A result set describing the query's or command's result.
|
|
*/
|
|
[[deprecated("The desc parameter is going away.")]] result
|
|
exec(std::string_view query, std::string_view desc);
|
|
|
|
/// Execute a command.
|
|
/**
|
|
* @param query Query or command to execute.
|
|
* @return A result set describing the query's or command's result.
|
|
*/
|
|
result exec(std::string_view query)
|
|
{
|
|
#include "pqxx/internal/ignore-deprecated-pre.hxx"
|
|
return exec(query, std::string_view{});
|
|
#include "pqxx/internal/ignore-deprecated-post.hxx"
|
|
}
|
|
|
|
/// Execute a command.
|
|
/**
|
|
* @param query Query or command to execute.
|
|
* @param desc Optional identifier for query, to help pinpoint SQL errors.
|
|
* @return A result set describing the query's or command's result.
|
|
*/
|
|
[[deprecated(
|
|
"Pass your query as a std::string_view, not stringstream.")]] result
|
|
exec(std::stringstream const &query, std::string_view desc)
|
|
{
|
|
#include "pqxx/internal/ignore-deprecated-pre.hxx"
|
|
return exec(query.str(), desc);
|
|
#include "pqxx/internal/ignore-deprecated-post.hxx"
|
|
}
|
|
|
|
/// Execute command, which should return zero rows of data.
|
|
/** Works like @ref exec, but fails if the result contains data. It still
|
|
* returns a result, however, which may contain useful metadata.
|
|
*
|
|
* @throw unexpected_rows If the query returned the wrong number of rows.
|
|
*/
|
|
[[deprecated("The desc parameter is going away.")]] result
|
|
exec0(zview query, std::string_view desc)
|
|
{
|
|
#include "pqxx/internal/ignore-deprecated-pre.hxx"
|
|
return exec_n(0, query, desc);
|
|
#include "pqxx/internal/ignore-deprecated-post.hxx"
|
|
}
|
|
|
|
/// Execute command, which should return zero rows of data.
|
|
/** Works like @ref exec, but fails if the result contains data. It still
|
|
* returns a result, however, which may contain useful metadata.
|
|
*
|
|
* @throw unexpected_rows If the query returned the wrong number of rows.
|
|
*/
|
|
result exec0(zview query) { return exec_n(0, query); }
|
|
|
|
/// Execute command returning a single row of data.
|
|
/** Works like @ref exec, but requires the result to contain exactly one row.
|
|
* The row can be addressed directly, without the need to find the first row
|
|
* in a result set.
|
|
*
|
|
* @throw unexpected_rows If the query returned the wrong number of rows.
|
|
*/
|
|
[[deprecated("The desc parameter is going away.")]] row
|
|
exec1(zview query, std::string_view desc)
|
|
{
|
|
#include "pqxx/internal/ignore-deprecated-pre.hxx"
|
|
return exec_n(1, query, desc).front();
|
|
#include "pqxx/internal/ignore-deprecated-post.hxx"
|
|
}
|
|
|
|
/// Execute command returning a single row of data.
|
|
/** Works like @ref exec, but requires the result to contain exactly one row.
|
|
* The row can be addressed directly, without the need to find the first row
|
|
* in a result set.
|
|
*
|
|
* @throw unexpected_rows If the query returned the wrong number of rows.
|
|
*/
|
|
row exec1(zview query) { return exec_n(1, query).front(); }
|
|
|
|
/// Execute command, expect given number of rows.
|
|
/** Works like @ref exec, but checks that the result has exactly the expected
|
|
* number of rows.
|
|
*
|
|
* @throw unexpected_rows If the query returned the wrong number of rows.
|
|
*/
|
|
[[deprecated("The desc parameter is going away.")]] result
|
|
exec_n(result::size_type rows, zview query, std::string_view desc);
|
|
|
|
/// Execute command, expect given number of rows.
|
|
/** Works like @ref exec, but checks that the result has exactly the expected
|
|
* number of rows.
|
|
*
|
|
* @throw unexpected_rows If the query returned the wrong number of rows.
|
|
*/
|
|
result exec_n(result::size_type rows, zview query)
|
|
{
|
|
#include "pqxx/internal/ignore-deprecated-pre.hxx"
|
|
return exec_n(rows, query, std::string_view{});
|
|
#include "pqxx/internal/ignore-deprecated-post.hxx"
|
|
}
|
|
|
|
/// Perform query, expecting exactly 1 row with 1 field, and convert it.
|
|
/** This is convenience shorthand for querying exactly one value from the
|
|
* database. It returns that value, converted to the type you specify.
|
|
*/
|
|
template<typename TYPE>
|
|
[[deprecated("The desc parameter is going away.")]] TYPE
|
|
query_value(zview query, std::string_view desc)
|
|
{
|
|
#include "pqxx/internal/ignore-deprecated-pre.hxx"
|
|
row const r{exec1(query, desc)};
|
|
#include "pqxx/internal/ignore-deprecated-post.hxx"
|
|
if (std::size(r) != 1)
|
|
throw usage_error{internal::concat(
|
|
"Queried single value from result with ", std::size(r), " columns.")};
|
|
return r[0].as<TYPE>();
|
|
}
|
|
|
|
/// Perform query, expecting exactly 1 row with 1 field, and convert it.
|
|
/** This is convenience shorthand for querying exactly one value from the
|
|
* database. It returns that value, converted to the type you specify.
|
|
*/
|
|
template<typename TYPE> TYPE query_value(zview query)
|
|
{
|
|
row const r{exec1(query)};
|
|
if (std::size(r) != 1)
|
|
throw usage_error{internal::concat(
|
|
"Queried single value from result with ", std::size(r), " columns.")};
|
|
return r[0].as<TYPE>();
|
|
}
|
|
|
|
/// Execute a query, and loop over the results row by row.
|
|
/** Converts the rows to `std::tuple`, of the column types you specify.
|
|
*
|
|
* Use this with a range-based "for" loop. It executes the query, and
|
|
* directly maps the resulting rows onto a `std::tuple` of the types you
|
|
* specify. It starts before all the data from the server is in, so if your
|
|
* network connection to the server breaks while you're iterating, you'll get
|
|
* an exception partway through.
|
|
*
|
|
* The stream lives entirely within the lifetime of the transaction. Make
|
|
* sure you destroy the stream before you destroy the transaction. Either
|
|
* iterate the stream all the way to the end, or destroy first the stream
|
|
* and then the transaction without touching either in any other way. Until
|
|
* the stream has finished, the transaction is in a special state where it
|
|
* cannot execute queries.
|
|
*
|
|
* As a special case, tuple may contain `std::string_view` fields, but the
|
|
* strings to which they point will only remain valid until you extract the
|
|
* next row. After that, the memory holding the string may be overwritten or
|
|
* deallocated.
|
|
*
|
|
* If any of the columns can be null, and the C++ type to which it translates
|
|
* does not have a null value, wrap the type in `std::optional` (or if
|
|
* you prefer, `std::shared_ptr` or `std::unique_ptr)`. These templates
|
|
* do recognise null values, and libpqxx will know how to convert to them.
|
|
*
|
|
* The connection is in a special state until the iteration finishes. So if
|
|
* it does not finish due to a `break` or a `return` or an exception, then
|
|
* the entire connection becomes effectively unusable.
|
|
*
|
|
* Querying in this way is faster than the `exec()` methods for larger
|
|
* results (but slower for small ones). You can start processing rows before
|
|
* the full result is in. Also, `stream()` scales better in terms of memory
|
|
* usage. Where @ref exec() reads the entire result into memory at once,
|
|
* `stream()` will read and process one row at at a time.
|
|
*
|
|
* Your query executes as part of a COPY command, not as a stand-alone query,
|
|
* so there are limitations to what you can do in the query. It can be
|
|
* either a SELECT or VALUES query; or an INSERT, UPDATE, or DELETE with a
|
|
* RETURNING clause. See the documentation for PostgreSQL's COPY command for
|
|
* the details:
|
|
*
|
|
* https://www.postgresql.org/docs/current/sql-copy.html
|
|
*
|
|
* Iterating in this way does require each of the field types you pass to be
|
|
* default-constructible, copy-constructible, and assignable. These
|
|
* requirements may be loosened once libpqxx moves on to C++20.
|
|
*/
|
|
template<typename... TYPE>
|
|
[[nodiscard]] auto stream(std::string_view query) &
|
|
{
|
|
// Tricky: std::make_unique() supports constructors but not RVO functions.
|
|
return pqxx::internal::owning_stream_input_iteration<TYPE...>{
|
|
std::unique_ptr<stream_from>{
|
|
new stream_from{stream_from::query(*this, query)}}};
|
|
}
|
|
|
|
// C++20: Concept like std::invocable, but without specifying param types.
|
|
/// Perform a streaming query, and for each result row, call `func`.
|
|
/** Here, `func` can be a function, a `std::function`, a lambda, or an
|
|
* object that supports the function call operator. Of course `func` must
|
|
* have an unambiguous signature; it can't be overloaded or generic.
|
|
*
|
|
* The `for_each` function executes `query` in a stream using
|
|
* @ref pqxx::stream_from. Every time a row of data comes in from the
|
|
* server, it converts the row's fields to the types of `func`'s respective
|
|
* parameters, and calls `func` with those values.
|
|
*
|
|
* This will not work for all queries, but straightforward `SELECT` and
|
|
* `UPDATE ... RETURNING` queries should work. Consult the documentation for
|
|
* @ref pqxx::stream_from and PostgreSQL's underlying `COPY` command for the
|
|
* full details.
|
|
*
|
|
* Streaming a query like this is likely to be slower than the @ref exec()
|
|
* functions for small result sets, but faster for large result sets. So if
|
|
* performance matters, you'll want to use `for_each` if you query large
|
|
* amounts of data, but not if you do lots of queries with small outputs.
|
|
*/
|
|
template<typename CALLABLE>
|
|
inline auto for_each(std::string_view query, CALLABLE &&func)
|
|
{
|
|
using param_types =
|
|
pqxx::internal::strip_types_t<pqxx::internal::args_t<CALLABLE>>;
|
|
param_types const *const sample{nullptr};
|
|
auto data_stream{stream_like(query, sample)};
|
|
for (auto const &fields : data_stream) std::apply(func, fields);
|
|
}
|
|
|
|
/**
|
|
* @name Parameterized statements
|
|
*
|
|
* You'll often need parameters in the queries you execute: "select the
|
|
* car with this licence plate." If the parameter is a string, you need to
|
|
* quote it and escape any special characters inside it, or it may become a
|
|
* target for an SQL injection attack. If it's an integer (for example),
|
|
* you need to convert it to a string, but in the database's format, without
|
|
* locale-specific niceties like "," separators between the thousands.
|
|
*
|
|
* Parameterised statements are an easier and safer way to do this. They're
|
|
* like prepared statements, but for a single use. You don't need to name
|
|
* them, and you don't need to prepare them first.
|
|
*
|
|
* Your query will include placeholders like `$1` and `$2` etc. in the places
|
|
* where you want the arguments to go. Then, you pass the argument values
|
|
* and the actual query is constructed for you.
|
|
*
|
|
* Pass the exact right number of parameters, and in the right order. The
|
|
* parameters in the query don't have to be neatly ordered from `$1` to
|
|
* `$2` to `$3` - but you must pass the argument for `$1` first, the one
|
|
* for `$2` second, etc.
|
|
*
|
|
* @warning Beware of "nul" bytes. Any string you pass as a parameter will
|
|
* end at the first char with value zero. If you pass a string that contains
|
|
* a zero byte, the last byte in the value will be the one just before the
|
|
* zero.
|
|
*/
|
|
//@{
|
|
/// Execute an SQL statement with parameters.
|
|
template<typename... Args> result exec_params(zview query, Args &&...args)
|
|
{
|
|
params pp(args...);
|
|
return internal_exec_params(query, pp.make_c_params());
|
|
}
|
|
|
|
// Execute parameterised statement, expect a single-row result.
|
|
/** @throw unexpected_rows if the result does not consist of exactly one row.
|
|
*/
|
|
template<typename... Args> row exec_params1(zview query, Args &&...args)
|
|
{
|
|
return exec_params_n(1, query, std::forward<Args>(args)...).front();
|
|
}
|
|
|
|
// Execute parameterised statement, expect a result with zero rows.
|
|
/** @throw unexpected_rows if the result contains rows.
|
|
*/
|
|
template<typename... Args> result exec_params0(zview query, Args &&...args)
|
|
{
|
|
return exec_params_n(0, query, std::forward<Args>(args)...);
|
|
}
|
|
|
|
// Execute parameterised statement, expect exactly a given number of rows.
|
|
/** @throw unexpected_rows if the result contains the wrong number of rows.
|
|
*/
|
|
template<typename... Args>
|
|
result exec_params_n(std::size_t rows, zview query, Args &&...args)
|
|
{
|
|
auto const r{exec_params(query, std::forward<Args>(args)...)};
|
|
check_rowcount_params(rows, std::size(r));
|
|
return r;
|
|
}
|
|
//@}
|
|
|
|
/**
|
|
* @name Prepared statements
|
|
*
|
|
* These are very similar to parameterised statements. The difference is
|
|
* that you prepare them in advance, giving them identifying names. You can
|
|
* then call them by these names, passing in the argument values appropriate
|
|
* for that call.
|
|
*
|
|
* You prepare a statement on the connection, using
|
|
* @ref pqxx::connection::prepare(). But you then call the statement in a
|
|
* transaction, using the functions you see here.
|
|
*
|
|
* Never try to prepare, execute, or unprepare a prepared statement manually
|
|
* using direct SQL queries when you also use the libpqxx equivalents. For
|
|
* any given statement, either prepare, manage, and execute it through the
|
|
* dedicated libpqxx functions; or do it all directly in SQL. Don't mix the
|
|
* two, or the code may get confused.
|
|
*
|
|
* See \ref prepared for a full discussion.
|
|
*
|
|
* @warning Beware of "nul" bytes. Any string you pass as a parameter will
|
|
* end at the first char with value zero. If you pass a string that contains
|
|
* a zero byte, the last byte in the value will be the one just before the
|
|
* zero. If you need a zero byte, you're dealing with binary strings, not
|
|
* regular strings. Represent binary strings on the SQL side as `BYTEA`
|
|
* (or as large objects). On the C++ side, use types like
|
|
* `std::basic_string<std::byte>` or `std::basic_string_view<std::byte>`
|
|
* or (in C++20) `std::vector<std::byte>`. Also, consider large objects on
|
|
* the SQL side and @ref blob on the C++ side.
|
|
*/
|
|
//@{
|
|
|
|
/// Execute a prepared statement, with optional arguments.
|
|
template<typename... Args>
|
|
result exec_prepared(zview statement, Args &&...args)
|
|
{
|
|
params pp(args...);
|
|
return internal_exec_prepared(statement, pp.make_c_params());
|
|
}
|
|
|
|
/// Execute a prepared statement, and expect a single-row result.
|
|
/** @throw pqxx::unexpected_rows if the result was not exactly 1 row.
|
|
*/
|
|
template<typename... Args>
|
|
row exec_prepared1(zview statement, Args &&...args)
|
|
{
|
|
return exec_prepared_n(1, statement, std::forward<Args>(args)...).front();
|
|
}
|
|
|
|
/// Execute a prepared statement, and expect a result with zero rows.
|
|
/** @throw pqxx::unexpected_rows if the result contained rows.
|
|
*/
|
|
template<typename... Args>
|
|
result exec_prepared0(zview statement, Args &&...args)
|
|
{
|
|
return exec_prepared_n(0, statement, std::forward<Args>(args)...);
|
|
}
|
|
|
|
/// Execute a prepared statement, expect a result with given number of rows.
|
|
/** @throw pqxx::unexpected_rows if the result did not contain exactly the
|
|
* given number of rows.
|
|
*/
|
|
template<typename... Args>
|
|
result
|
|
exec_prepared_n(result::size_type rows, zview statement, Args &&...args)
|
|
{
|
|
auto const r{exec_prepared(statement, std::forward<Args>(args)...)};
|
|
check_rowcount_prepared(statement, rows, std::size(r));
|
|
return r;
|
|
}
|
|
|
|
//@}
|
|
|
|
/**
|
|
* @name Error/warning output
|
|
*/
|
|
//@{
|
|
/// Have connection process a warning message.
|
|
void process_notice(char const msg[]) const { m_conn.process_notice(msg); }
|
|
/// Have connection process a warning message.
|
|
void process_notice(zview msg) const { m_conn.process_notice(msg); }
|
|
//@}
|
|
|
|
/// The connection in which this transaction lives.
|
|
[[nodiscard]] constexpr connection &conn() const noexcept { return m_conn; }
|
|
|
|
/// Set session variable using SQL "SET" command.
|
|
/** @deprecated To set a transaction-local variable, execute an SQL `SET`
|
|
* command. To set a session variable, use the connection's
|
|
* @ref set_session_var function.
|
|
*
|
|
* @warning When setting a string value, you must make sure that the string
|
|
* is "safe." If you call @ref quote() on the string, it will return a
|
|
* safely escaped and quoted version for use as an SQL literal.
|
|
*
|
|
* @warning This function executes SQL. Do not try to set or get variables
|
|
* while a pipeline or table stream is active.
|
|
*
|
|
* @param var The variable to set.
|
|
* @param value The new value to store in the variable. This can be any SQL
|
|
* expression.
|
|
*/
|
|
[[deprecated(
|
|
"Set transaction-local variables using SQL SET statements.")]] void
|
|
set_variable(std::string_view var, std::string_view value);
|
|
|
|
/// Read session variable using SQL "SHOW" command.
|
|
/** @warning This executes SQL. Do not try to set or get variables while a
|
|
* pipeline or table stream is active.
|
|
*/
|
|
[[deprecated("Read variables using SQL SHOW statements.")]] std::string
|
|
get_variable(std::string_view);
|
|
|
|
// C++20: constexpr.
|
|
/// Transaction name, if you passed one to the constructor; or empty string.
|
|
[[nodiscard]] std::string_view name() const &noexcept { return m_name; }
|
|
|
|
protected:
|
|
/// Create a transaction (to be called by implementation classes only).
|
|
/** The name, if nonempty, must begin with a letter and may contain letters
|
|
* and digits only.
|
|
*/
|
|
transaction_base(
|
|
connection &c, std::string_view tname,
|
|
std::shared_ptr<std::string> rollback_cmd) :
|
|
m_conn{c}, m_name{tname}, m_rollback_cmd{rollback_cmd}
|
|
{}
|
|
|
|
/// Create a transaction (to be called by implementation classes only).
|
|
/** Its rollback command will be "ROLLBACK".
|
|
*
|
|
* The name, if nonempty, must begin with a letter and may contain letters
|
|
* and digits only.
|
|
*/
|
|
transaction_base(connection &c, std::string_view tname);
|
|
|
|
/// Create a transaction (to be called by implementation classes only).
|
|
explicit transaction_base(connection &c);
|
|
|
|
/// Register this transaction with the connection.
|
|
void register_transaction();
|
|
|
|
/// End transaction. To be called by implementing class' destructor.
|
|
void close() noexcept;
|
|
|
|
/// To be implemented by derived implementation class: commit transaction.
|
|
virtual void do_commit() = 0;
|
|
|
|
/// Transaction type-specific way of aborting a transaction.
|
|
/** @warning This will become "final", since this function can be called
|
|
* from the implementing class destructor.
|
|
*/
|
|
virtual void do_abort();
|
|
|
|
/// Set the rollback command.
|
|
void set_rollback_cmd(std::shared_ptr<std::string> cmd)
|
|
{
|
|
m_rollback_cmd = cmd;
|
|
}
|
|
|
|
/// Execute query on connection directly.
|
|
result direct_exec(std::string_view, std::string_view desc = ""sv);
|
|
result
|
|
direct_exec(std::shared_ptr<std::string>, std::string_view desc = ""sv);
|
|
|
|
private:
|
|
enum class status
|
|
{
|
|
active,
|
|
aborted,
|
|
committed,
|
|
in_doubt
|
|
};
|
|
|
|
PQXX_PRIVATE void check_pending_error();
|
|
|
|
result
|
|
internal_exec_prepared(zview statement, internal::c_params const &args);
|
|
|
|
result internal_exec_params(zview query, internal::c_params const &args);
|
|
|
|
/// Throw unexpected_rows if prepared statement returned wrong no. of rows.
|
|
void check_rowcount_prepared(
|
|
zview statement, result::size_type expected_rows,
|
|
result::size_type actual_rows);
|
|
|
|
/// Throw unexpected_rows if wrong row count from parameterised statement.
|
|
void
|
|
check_rowcount_params(std::size_t expected_rows, std::size_t actual_rows);
|
|
|
|
/// Describe this transaction to humans, e.g. "transaction 'foo'".
|
|
[[nodiscard]] std::string description() const;
|
|
|
|
friend class pqxx::internal::gate::transaction_transaction_focus;
|
|
PQXX_PRIVATE void register_focus(transaction_focus *);
|
|
PQXX_PRIVATE void unregister_focus(transaction_focus *) noexcept;
|
|
PQXX_PRIVATE void register_pending_error(zview) noexcept;
|
|
PQXX_PRIVATE void register_pending_error(std::string &&) noexcept;
|
|
|
|
/// Like @ref stream(), but takes a tuple rather than a parameter pack.
|
|
template<typename... ARGS>
|
|
auto stream_like(std::string_view query, std::tuple<ARGS...> const *)
|
|
{
|
|
return stream<ARGS...>(query);
|
|
}
|
|
|
|
connection &m_conn;
|
|
|
|
/// Current "focus": a pipeline, a nested transaction, a stream...
|
|
/** This pointer is used for only one purpose: sanity checks against mistakes
|
|
* such as opening one while another is still active.
|
|
*/
|
|
transaction_focus const *m_focus = nullptr;
|
|
|
|
status m_status = status::active;
|
|
bool m_registered = false;
|
|
std::string m_name;
|
|
std::string m_pending_error;
|
|
|
|
/// SQL command for aborting this type of transaction.
|
|
std::shared_ptr<std::string> m_rollback_cmd;
|
|
|
|
static constexpr std::string_view s_type_name{"transaction"sv};
|
|
};
|
|
|
|
|
|
// C++20: Can borrowed_range help?
|
|
/// Forbidden specialisation: underlying buffer immediately goes out of scope.
|
|
template<>
|
|
std::string_view transaction_base::query_value<std::string_view>(
|
|
zview query, std::string_view desc) = delete;
|
|
/// Forbidden specialisation: underlying buffer immediately goes out of scope.
|
|
template<>
|
|
zview transaction_base::query_value<zview>(
|
|
zview query, std::string_view desc) = delete;
|
|
|
|
} // namespace pqxx
|
|
|
|
|
|
namespace pqxx::internal
|
|
{
|
|
/// The SQL command for starting a given type of transaction.
|
|
template<pqxx::isolation_level isolation, pqxx::write_policy rw>
|
|
extern const zview begin_cmd;
|
|
|
|
// These are not static members, so "constexpr" does not imply "inline".
|
|
template<>
|
|
inline constexpr zview begin_cmd<read_committed, write_policy::read_write>{
|
|
"BEGIN"_zv};
|
|
template<>
|
|
inline constexpr zview begin_cmd<read_committed, write_policy::read_only>{
|
|
"BEGIN READ ONLY"_zv};
|
|
template<>
|
|
inline constexpr zview begin_cmd<repeatable_read, write_policy::read_write>{
|
|
"BEGIN ISOLATION LEVEL REPEATABLE READ"_zv};
|
|
template<>
|
|
inline constexpr zview begin_cmd<repeatable_read, write_policy::read_only>{
|
|
"BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY"_zv};
|
|
template<>
|
|
inline constexpr zview begin_cmd<serializable, write_policy::read_write>{
|
|
"BEGIN ISOLATION LEVEL SERIALIZABLE"_zv};
|
|
template<>
|
|
inline constexpr zview begin_cmd<serializable, write_policy::read_only>{
|
|
"BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY"_zv};
|
|
} // namespace pqxx::internal
|
|
#endif
|