/* 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 , not ." #endif #include #include /* 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 [[nodiscard]] auto esc(ARGS &&...args) const { return conn().esc(std::forward(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 [[nodiscard]] auto esc_raw(ARGS &&...args) const { return conn().esc_raw(std::forward(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 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 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 [[nodiscard]] std::string quote(T const &t) const { return conn().quote(t); } [[deprecated( "Use std::basic_string 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::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::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 [[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 [[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(); } /// 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 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(); } /// 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 [[nodiscard]] auto stream(std::string_view query) & { // Tricky: std::make_unique() supports constructors but not RVO functions. return pqxx::internal::owning_stream_input_iteration{ std::unique_ptr{ 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 inline auto for_each(std::string_view query, CALLABLE &&func) { using param_types = pqxx::internal::strip_types_t>; 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 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 row exec_params1(zview query, Args &&...args) { return exec_params_n(1, query, std::forward(args)...).front(); } // Execute parameterised statement, expect a result with zero rows. /** @throw unexpected_rows if the result contains rows. */ template result exec_params0(zview query, Args &&...args) { return exec_params_n(0, query, std::forward(args)...); } // Execute parameterised statement, expect exactly a given number of rows. /** @throw unexpected_rows if the result contains the wrong number of rows. */ template result exec_params_n(std::size_t rows, zview query, Args &&...args) { auto const r{exec_params(query, std::forward(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` or `std::basic_string_view` * or (in C++20) `std::vector`. Also, consider large objects on * the SQL side and @ref blob on the C++ side. */ //@{ /// Execute a prepared statement, with optional arguments. template 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 row exec_prepared1(zview statement, Args &&...args) { return exec_prepared_n(1, statement, std::forward(args)...).front(); } /// Execute a prepared statement, and expect a result with zero rows. /** @throw pqxx::unexpected_rows if the result contained rows. */ template result exec_prepared0(zview statement, Args &&...args) { return exec_prepared_n(0, statement, std::forward(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 result exec_prepared_n(result::size_type rows, zview statement, Args &&...args) { auto const r{exec_prepared(statement, std::forward(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 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 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_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 auto stream_like(std::string_view query, std::tuple const *) { return stream(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 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( zview query, std::string_view desc) = delete; /// Forbidden specialisation: underlying buffer immediately goes out of scope. template<> zview transaction_base::query_value( zview query, std::string_view desc) = delete; } // namespace pqxx namespace pqxx::internal { /// The SQL command for starting a given type of transaction. template extern const zview begin_cmd; // These are not static members, so "constexpr" does not imply "inline". template<> inline constexpr zview begin_cmd{ "BEGIN"_zv}; template<> inline constexpr zview begin_cmd{ "BEGIN READ ONLY"_zv}; template<> inline constexpr zview begin_cmd{ "BEGIN ISOLATION LEVEL REPEATABLE READ"_zv}; template<> inline constexpr zview begin_cmd{ "BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY"_zv}; template<> inline constexpr zview begin_cmd{ "BEGIN ISOLATION LEVEL SERIALIZABLE"_zv}; template<> inline constexpr zview begin_cmd{ "BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY"_zv}; } // namespace pqxx::internal #endif