/** 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-2019, 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 #include "pqxx/compiler-public.hxx" #include "pqxx/compiler-internal-pre.hxx" /* 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_base.hxx" #include "pqxx/internal/encoding_group.hxx" #include "pqxx/isolation.hxx" #include "pqxx/result.hxx" #include "pqxx/row.hxx" // Methods tested in eg. test module test01 are marked with "//[t01]". namespace pqxx { namespace internal { class sql_cursor; class PQXX_LIBEXPORT transactionfocus : public virtual namedclass { public: explicit transactionfocus(transaction_base &t) : namedclass{"transactionfocus"}, m_trans{t}, m_registered{false} { } transactionfocus() =delete; transactionfocus(const transactionfocus &) =delete; transactionfocus &operator=(const transactionfocus &) =delete; protected: void register_me(); void unregister_me() noexcept; void reg_pending_error(const std::string &) noexcept; bool registered() const noexcept { return m_registered; } transaction_base &m_trans; private: bool m_registered; }; /// Helper class to construct an invocation of a parameterised statement. /** @deprecated Use @c exec_params and friends instead. */ class PQXX_LIBEXPORT parameterized_invocation : statement_parameters { public: PQXX_DEPRECATED parameterized_invocation( connection_base &, const std::string &query); parameterized_invocation &operator()() { add_param(); return *this; } parameterized_invocation &operator()(const binarystring &v) { add_binary_param(v, true); return *this; } template parameterized_invocation &operator()(const T &v) { add_param(v, true); return *this; } parameterized_invocation &operator()(const binarystring &v, bool nonnull) { add_binary_param(v, nonnull); return *this; } template parameterized_invocation &operator()(const T &v, bool nonnull) { add_param(v, nonnull); return *this; } result exec(); private: /// Not allowed parameterized_invocation &operator=(const parameterized_invocation &) =delete; connection_base &m_home; const std::string m_query; }; } // namespace internal namespace internal { namespace gate { class transaction_subtransaction; class transaction_tablereader; class transaction_sql_cursor; class transaction_stream_from; class transaction_tablewriter; class transaction_stream_to; class transaction_transactionfocus; } // namespace internal::gate } // namespace internal /** * @defgroup transaction 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 transaction * * Abstract base class for all transaction types. */ class PQXX_LIBEXPORT PQXX_NOVTABLE transaction_base : public virtual internal::namedclass { public: /// If nothing else is known, our isolation level is at least read_committed using isolation_tag = isolation_traits; transaction_base() =delete; transaction_base(const transaction_base &) =delete; transaction_base &operator=(const transaction_base &) =delete; virtual ~transaction_base() =0; //[t01] /// Commit the transaction /** Unless this function is called explicitly, the transaction will not be * committed (actually the nontransaction implementation breaks this rule, * hence the name). * * Once this function returns, the whole transaction will typically be * irrevocably completed in the database. There is also, however, a minute * risk that the connection to the database may be lost at just the wrong * moment. In that case, libpqxx may be unable to determine whether the * transaction was completed or aborted and an in_doubt_error will be thrown * to make this fact known to the caller. The robusttransaction * implementation takes some special precautions to reduce this risk. */ void commit(); //[t01] /// Abort the transaction /** No special effort is required to call this function; it will be called * implicitly when the transaction is destructed. */ void abort(); //[t10] /** * @ingroup escaping-functions */ //@{ /// Escape string for use as SQL string literal in this transaction std::string esc(const char str[]) const { return conn().esc(str); } /// Escape string for use as SQL string literal in this transaction std::string esc(const char str[], size_t maxlen) const { return conn().esc(str, maxlen); } /// Escape string for use as SQL string literal in this transaction std::string esc(const std::string &str) const { return conn().esc(str); } /// 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, they 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. */ std::string esc_raw(const unsigned char data[], size_t len) const //[t62] { return conn().esc_raw(data, len); } /// Escape binary data for use as SQL string literal in this transaction std::string esc_raw(const std::string &) const; //[t62] /// 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. */ std::string unesc_raw(const std::string &text) const { return conn().unesc_raw(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. */ std::string unesc_raw(const char *text) const { return conn().unesc_raw(text); } /// Represent object as SQL string, including quoting & escaping. /** Nulls are recognized and represented as SQL nulls. */ template std::string quote(const T &t) const { return conn().quote(t); } /// Binary-escape and quote a binarystring for use as an SQL constant. std::string quote_raw(const unsigned char str[], size_t len) const { return conn().quote_raw(str, len); } std::string quote_raw(const std::string &str) const; /// Escape an SQL identifier for use in a query. std::string quote_name(const std::string &identifier) const { return conn().quote_name(identifier); } /// Escape string for literal LIKE match. std::string esc_like(const std::string &str, char escape_char='\\') const { return conn().esc_like(str, escape_char); } //@} /// Execute query /** Perform a query in this transaction. * * This is one of the most important functions in libpqxx. * * Most libpqxx exceptions can be thrown from here, 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 will be * derived from std::exception, however, and all libpqxx-specific exception * types are derived from pqxx::pqxx_exception. * * @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 */ result exec( const std::string &Query, const std::string &Desc=std::string{}); //[t01] result exec( const std::stringstream &Query, const std::string &Desc=std::string{}) { return exec(Query.str(), Desc); } /// Execute query, which should zero rows of data. /** Works like 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( const std::string &Query, const std::string &Desc=std::string{}) { return exec_n(0, Query, Desc); } /// Execute query returning a single row of data. /** Works like 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(const std::string &Query, const std::string &Desc=std::string{}) { return exec_n(1, Query, Desc).front(); } /// Execute query, expect given number of rows. /** Works like exec, but checks that the number of rows is exactly what's * expected. * * @throw unexpected_rows If the query returned the wrong number of rows. */ result exec_n( size_t rows, const std::string &Query, const std::string &Desc=std::string{}); /** * @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 @c $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 @c $1 to * @c $2 to @c $3 - but you must pass the argument for @c $1 first, the one * for @c $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 @c std::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(const std::string &query, Args &&...args) { return internal_exec_params( query, internal::params(std::forward(args)...)); } // 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(const std::string &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(const std::string &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(size_t rows, const std::string &query, Args &&...args) { const auto r = exec_params(query, std::forward(args)...); check_rowcount_params(rows, r.size()); return r; } /// Parameterize a statement. @deprecated Use @c exec_params instead. /* Use this to build up a parameterized statement invocation, then invoke it * using @c exec() * * Example: @c trans.parameterized("SELECT $1 + 1")(1).exec(); * * This is the old, pre-C++11 way of handling parameterised statements. As * of libpqxx 6.0, it's made much easier using variadic templates. */ PQXX_DEPRECATED internal::parameterized_invocation parameterized(const std::string &query); //@} /** * @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 * @c pqxx::connection_base::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. Always use the functions provided by libpqxx. * * 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 @c std::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, consider using * pqxx::binarystring and/or SQL's @c bytea type. */ //@{ /// Execute a prepared statement, with optional arguments. template result exec_prepared(const std::string &statement, Args&&... args) { return internal_exec_prepared( statement, internal::params(std::forward(args)...), result_format::text); } /// Execute a prepared statement, with optional arguments. template result exec_prepared_binary(const std::string &statement, Args&&... args) { return internal_exec_prepared( statement, internal::params(std::forward(args)...), result_format::binary); } /// 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(const std::string &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(const std::string &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( size_t rows, const std::string &statement, Args&&... args) { const auto r = exec_prepared(statement, std::forward(args)...); check_rowcount_prepared(statement, rows, r.size()); return r; } /// Execute prepared statement. @deprecated Use exec_prepared instead. /** Just like param_declaration is a helper class that lets you tag parameter * declarations onto the statement declaration, the invocation class returned * here lets you tag parameter values onto the call: * * @code * result run_mystatement(transaction_base &T) * { * return T.exec_prepared("mystatement", "param1", 2, nullptr, 4); * } * @endcode * * Here, parameter 1 (written as "$1" in the statement's body) is a * string that receives the value "param1"; the second parameter is an integer * with the value 2; the third receives a null, making its type irrelevant; * and number 4 again is an integer. The ultimate invocation of exec() is * essential; if you forget this, nothing happens. * * To see whether any prepared statement has been defined under a given name, * use: * * @code * T.prepared("mystatement").exists() * @endcode * * @warning Do not try to execute a prepared statement manually through direct * SQL statements. This is likely not to work, and even if it does, is likely * to be slower than using the proper libpqxx functions. Also, libpqxx knows * how to emulate prepared statements if some part of the infrastructure does * not support them. * * @warning Actual definition of the prepared statement on the backend may be * deferred until its first use, which means that any errors in the prepared * statement may not show up until it is executed--and perhaps abort the * ongoing transaction in the process. * * If you leave out the statement name, the call refers to the nameless * statement instead. */ PQXX_DEPRECATED prepare::invocation prepared(const std::string &statement=std::string{}); //@} /** * @name Error/warning output */ //@{ /// Have connection process warning message void process_notice(const char Msg[]) const //[t14] { m_conn.process_notice(Msg); } /// Have connection process warning message void process_notice(const std::string &Msg) const //[t14] { m_conn.process_notice(Msg); } //@} /// Connection this transaction is running in connection_base &conn() const { return m_conn; } //[t04] /// Set session variable in this connection /** The new value is typically forgotten if the transaction aborts. * However nontransaction is an exception to this rule: in that case the set * value will be kept regardless. Also, if the connection ever needs to be * recovered, a value you set in a nontransaction will not be restored. * @param Var The variable to set * @param Val The new value to store in the variable */ void set_variable(const std::string &Var, const std::string &Val); //[t61] /// Get currently applicable value of variable /** First consults an internal cache of variables that have been set (whether * in the ongoing transaction or in the connection) using the set_variable * functions. If it is not found there, the database is queried. * * @warning Do not mix the set_variable with raw "SET" queries, and do not * try to set or get variables while a pipeline or table stream is active. * * @warning This function used to be declared as @c const but isn't anymore. */ std::string get_variable(const std::string &); //[t61] protected: /// Create a transaction (to be called by implementation classes only) /** The optional name, if nonempty, must begin with a letter and may contain * letters and digits only. * * @param c The connection that this transaction is to act on. * @param direct Running directly in connection context (i.e. not nested)? */ explicit transaction_base(connection_base &c, bool direct=true); /// Begin transaction (to be called by implementing class) /** Will typically be called from implementing class' constructor. */ void Begin(); /// End transaction. To be called by implementing class' destructor void End() noexcept; /// To be implemented by derived implementation class: start transaction virtual void do_begin() =0; /// To be implemented by derived implementation class: perform query virtual result do_exec(const char Query[]) =0; /// To be implemented by derived implementation class: commit transaction virtual void do_commit() =0; /// To be implemented by derived implementation class: abort transaction virtual void do_abort() =0; // For use by implementing class: /// Execute query on connection directly /** * @param C Query or command to execute * @param Retries Number of times to retry the query if it fails. Be * extremely careful with this option; if you retry in the middle of a * transaction, you may be setting up a new connection transparently and * executing the latter part of the transaction without a backend transaction * being active (and with the former part aborted). */ result direct_exec(const char C[], int Retries=0); /// Forget about any reactivation-blocking resources we tried to allocate void reactivation_avoidance_clear() noexcept {m_reactivation_avoidance.clear();} protected: /// Resources allocated in this transaction that make reactivation impossible /** This number may be negative! */ internal::reactivation_avoidance_counter m_reactivation_avoidance; private: /* A transaction goes through the following stages in its lifecycle: *
    *
  • nascent: the transaction hasn't actually begun yet. If our connection * fails at this stage, it may recover and the transaction can attempt to * establish itself again. *
  • active: the transaction has begun. Since no commit command has been * issued, abortion is implicit if the connection fails now. *
  • aborted: an abort has been issued; the transaction is terminated and * its changes to the database rolled back. It will accept no further * commands. *
  • committed: the transaction has completed successfully, meaning that a * commit has been issued. No further commands are accepted. *
  • in_doubt: the connection was lost at the exact wrong time, and there * is no way of telling whether the transaction was committed or aborted. *
* * Checking and maintaining state machine logic is the responsibility of the * base class (ie., this one). */ enum Status { st_nascent, st_active, st_aborted, st_committed, st_in_doubt }; /// Make sure transaction is opened on backend, if appropriate PQXX_PRIVATE void activate(); PQXX_PRIVATE void CheckPendingError(); template bool parm_is_null(T *p) const noexcept { return p == nullptr; } template bool parm_is_null(T) const noexcept { return false; } result internal_exec_prepared( const std::string &statement, const internal::params &args, result_format format); result internal_exec_params( const std::string &query, const internal::params &args); /// Throw unexpected_rows if prepared statement returned wrong no. of rows. void check_rowcount_prepared( const std::string &statement, size_t expected_rows, size_t actual_rows); /// Throw unexpected_rows if wrong row count from parameterised statement. void check_rowcount_params( size_t expected_rows, size_t actual_rows); friend class pqxx::internal::gate::transaction_transactionfocus; PQXX_PRIVATE void register_focus(internal::transactionfocus *); PQXX_PRIVATE void unregister_focus(internal::transactionfocus *) noexcept; PQXX_PRIVATE void register_pending_error(const std::string &) noexcept; friend class pqxx::internal::gate::transaction_tablereader; friend class pqxx::internal::gate::transaction_stream_from; PQXX_PRIVATE void BeginCopyRead(const std::string &, const std::string &); bool read_copy_line(std::string &); friend class pqxx::internal::gate::transaction_tablewriter; friend class pqxx::internal::gate::transaction_stream_to; PQXX_PRIVATE void BeginCopyWrite( const std::string &Table, const std::string &Columns); void write_copy_line(const std::string &); void end_copy_write(); friend class pqxx::internal::gate::transaction_subtransaction; connection_base &m_conn; internal::unique m_focus; Status m_status = st_nascent; bool m_registered = false; std::map m_vars; std::string m_pending_error; }; } // namespace pqxx #include "pqxx/compiler-internal-post.hxx" #endif