mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 10:56:12 +01:00 
			
		
		
		
	 053475fe4f
			
		
	
	
	053475fe4f
	
	
	
		
			
			This is the prerequisite patch to make interface of the class Reprepare_observer more similar to the one used by MySQL. This patch adds the method can_retry() to the class Reprepare_observer that returns true in case max. number of attempts to re-run a failing statement is not yet reached. To control the number of re-run attempts already done the data member m_attempt has been introduced. Doing this way, we encapsulate activity with incrementing a counter on every statement run and checking whether it reaches a limit or not inside implementation of the class Reprepare_observer instead duplicating such boiler plate code in every place where controlling for reaching a limit of max. number attempts for re-running failed statement is required.
		
			
				
	
	
		
			369 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifndef SQL_PREPARE_H
 | |
| #define SQL_PREPARE_H
 | |
| /* Copyright (c) 1995-2008 MySQL AB, 2009 Sun Microsystems, Inc.
 | |
|    Use is subject to license terms.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation; version 2 of the License.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
 | |
| 
 | |
| #include "sql_error.h"
 | |
| 
 | |
| 
 | |
| #define LAST_STMT_ID 0xFFFFFFFF
 | |
| #define STMT_ID_MASK 0x7FFFFFFF
 | |
| 
 | |
| class THD;
 | |
| struct LEX;
 | |
| 
 | |
| /**
 | |
|   An interface that is used to take an action when
 | |
|   the locking module notices that a table version has changed
 | |
|   since the last execution. "Table" here may refer to any kind of
 | |
|   table -- a base table, a temporary table, a view or an
 | |
|   information schema table.
 | |
| 
 | |
|   When we open and lock tables for execution of a prepared
 | |
|   statement, we must verify that they did not change
 | |
|   since statement prepare. If some table did change, the statement
 | |
|   parse tree *may* be no longer valid, e.g. in case it contains
 | |
|   optimizations that depend on table metadata.
 | |
| 
 | |
|   This class provides an interface (a method) that is
 | |
|   invoked when such a situation takes place.
 | |
|   The implementation of the method simply reports an error, but
 | |
|   the exact details depend on the nature of the SQL statement.
 | |
| 
 | |
|   At most 1 instance of this class is active at a time, in which
 | |
|   case THD::m_reprepare_observer is not NULL.
 | |
| 
 | |
|   @sa check_and_update_table_version() for details of the
 | |
|   version tracking algorithm 
 | |
| 
 | |
|   @sa Open_tables_state::m_reprepare_observer for the life cycle
 | |
|   of metadata observers.
 | |
| */
 | |
| 
 | |
| class Reprepare_observer
 | |
| {
 | |
| public:
 | |
|   /**
 | |
|     Check if a change of metadata is OK. In future
 | |
|     the signature of this method may be extended to accept the old
 | |
|     and the new versions, but since currently the check is very
 | |
|     simple, we only need the THD to report an error.
 | |
|   */
 | |
|   bool report_error(THD *thd);
 | |
|   bool is_invalidated() const { return m_invalidated; }
 | |
|   void reset_reprepare_observer() { m_invalidated= FALSE; }
 | |
| 
 | |
|   bool can_retry() const
 | |
|   {
 | |
|     // The method must be called only for a statement that is invalidated
 | |
|     assert(is_invalidated());
 | |
|     return m_attempt <= MAX_REPREPARE_ATTEMPTS;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   bool m_invalidated{false};
 | |
|   int m_attempt{0};
 | |
| 
 | |
|   static const int MAX_REPREPARE_ATTEMPTS= 3;
 | |
| };
 | |
| 
 | |
| 
 | |
| void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length);
 | |
| void mysqld_stmt_execute(THD *thd, char *packet, uint packet_length);
 | |
| void mysqld_stmt_execute_bulk(THD *thd, char *packet, uint packet_length);
 | |
| void mysqld_stmt_bulk_execute(THD *thd, char *packet, uint packet_length);
 | |
| void mysqld_stmt_close(THD *thd, char *packet);
 | |
| void mysql_sql_stmt_prepare(THD *thd);
 | |
| void mysql_sql_stmt_execute(THD *thd);
 | |
| void mysql_sql_stmt_execute_immediate(THD *thd);
 | |
| void mysql_sql_stmt_close(THD *thd);
 | |
| void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length);
 | |
| void mysqld_stmt_reset(THD *thd, char *packet);
 | |
| void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
 | |
| void reinit_stmt_before_use(THD *thd, LEX *lex);
 | |
| 
 | |
| my_bool bulk_parameters_iterations(THD *thd);
 | |
| my_bool bulk_parameters_set(THD *thd);
 | |
| /**
 | |
|   Execute a fragment of server code in an isolated context, so that
 | |
|   it doesn't leave any effect on THD. THD must have no open tables.
 | |
|   The code must not leave any open tables around.
 | |
|   The result of execution (if any) is stored in Ed_result.
 | |
| */
 | |
| 
 | |
| class Server_runnable
 | |
| {
 | |
| public:
 | |
|   virtual bool execute_server_code(THD *thd)= 0;
 | |
|   virtual ~Server_runnable();
 | |
| };
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Execute direct interface.
 | |
| 
 | |
|   @todo Implement support for prelocked mode.
 | |
| */
 | |
| 
 | |
| class Ed_row;
 | |
| 
 | |
| /**
 | |
|   Ed_result_set -- a container with result set rows.
 | |
|   @todo Implement support for result set metadata and
 | |
|   automatic type conversion.
 | |
| */
 | |
| 
 | |
| class Ed_result_set
 | |
| {
 | |
| public:
 | |
|   operator List<Ed_row>&() { return *m_rows; }
 | |
|   unsigned int size() const { return m_rows->elements; }
 | |
| 
 | |
|   Ed_result_set(List<Ed_row> *rows_arg, size_t column_count,
 | |
|                 MEM_ROOT *mem_root_arg);
 | |
| 
 | |
|   /** We don't call member destructors, they all are POD types. */
 | |
|   ~Ed_result_set() = default;
 | |
| 
 | |
|   size_t get_field_count() const { return m_column_count; }
 | |
| 
 | |
|   static void *operator new(size_t size, MEM_ROOT *mem_root)
 | |
|   { return alloc_root(mem_root, size); }
 | |
|   static void operator delete(void *ptr, size_t size) throw ();
 | |
|   static void operator delete(void *, MEM_ROOT *){}
 | |
| private:
 | |
|   Ed_result_set(const Ed_result_set &);        /* not implemented */
 | |
|   Ed_result_set &operator=(Ed_result_set &);   /* not implemented */
 | |
| private:
 | |
|   MEM_ROOT m_mem_root;
 | |
|   size_t m_column_count;
 | |
|   List<Ed_row> *m_rows;
 | |
|   Ed_result_set *m_next_rset;
 | |
|   friend class Ed_connection;
 | |
| };
 | |
| 
 | |
| 
 | |
| class Ed_connection
 | |
| {
 | |
| public:
 | |
|   /**
 | |
|     Construct a new "execute direct" connection.
 | |
| 
 | |
|     The connection can be used to execute SQL statements.
 | |
|     If the connection failed to initialize, the error
 | |
|     will be returned on the attempt to execute a statement.
 | |
| 
 | |
|     @pre thd  must have no open tables
 | |
|               while the connection is used. However,
 | |
|               Ed_connection works okay in LOCK TABLES mode.
 | |
|               Other properties of THD, such as the current warning
 | |
|               information, errors, etc. do not matter and are
 | |
|               preserved by Ed_connection. One thread may have many
 | |
|               Ed_connections created for it.
 | |
|   */
 | |
|   Ed_connection(THD *thd);
 | |
| 
 | |
|   /**
 | |
|     Execute one SQL statement.
 | |
| 
 | |
|     Until this method is executed, no other methods of
 | |
|     Ed_connection can be used. Life cycle of Ed_connection is:
 | |
| 
 | |
|     Initialized -> a statement has been executed ->
 | |
|     look at result, move to next result ->
 | |
|     look at result, move to next result ->
 | |
|     ...
 | |
|     moved beyond the last result == Initialized.
 | |
| 
 | |
|     This method can be called repeatedly. Once it's invoked,
 | |
|     results of the previous execution are lost.
 | |
| 
 | |
|     A result of execute_direct() can be either:
 | |
| 
 | |
|     - success, no result set rows. In this case get_field_count()
 | |
|     returns 0. This happens after execution of INSERT, UPDATE,
 | |
|     DELETE, DROP and similar statements. Some other methods, such
 | |
|     as get_affected_rows() can be used to retrieve additional
 | |
|     result information.
 | |
| 
 | |
|     - success, there are some result set rows (maybe 0). E.g.
 | |
|     happens after SELECT. In this case get_field_count() returns
 | |
|     the number of columns in a result set and store_result()
 | |
|     can be used to retrieve a result set..
 | |
| 
 | |
|     - an error, methods to retrieve error information can
 | |
|     be used.
 | |
| 
 | |
|     @return execution status
 | |
|     @retval FALSE  success, use get_field_count()
 | |
|                    to determine what to do next.
 | |
|     @retval TRUE   error, use get_last_error()
 | |
|                    to see the error number.
 | |
|   */
 | |
|   bool execute_direct(Protocol *p, LEX_STRING sql_text);
 | |
| 
 | |
|   /**
 | |
|     Same as the previous, but takes an instance of Server_runnable
 | |
|     instead of SQL statement text.
 | |
| 
 | |
|     @return execution status
 | |
|       
 | |
|     @retval  FALSE  success, use get_field_count() 
 | |
|                     if your code fragment is supposed to
 | |
|                     return a result set
 | |
|     @retval  TRUE   failure
 | |
|   */
 | |
|   bool execute_direct(Protocol *p, Server_runnable *server_runnable);
 | |
| 
 | |
|   /**
 | |
|     Get the number of affected (deleted, updated)
 | |
|     rows for the current statement. Can be
 | |
|     used for statements with get_field_count() == 0.
 | |
| 
 | |
|     @sa Documentation for C API function
 | |
|     mysql_affected_rows().
 | |
|   */
 | |
|   ulonglong get_affected_rows() const
 | |
|   {
 | |
|     return m_diagnostics_area.affected_rows();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|     Get the last insert id, if any.
 | |
| 
 | |
|     @sa Documentation for mysql_insert_id().
 | |
|   */
 | |
|   ulonglong get_last_insert_id() const
 | |
|   {
 | |
|     return m_diagnostics_area.last_insert_id();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|     Get the total number of warnings for the last executed
 | |
|     statement. Note, that there is only one warning list even
 | |
|     if a statement returns multiple results.
 | |
| 
 | |
|     @sa Documentation for C API function
 | |
|     mysql_num_warnings().
 | |
|   */
 | |
|   ulong get_warn_count() const
 | |
|   {
 | |
|     return m_diagnostics_area.warn_count();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|     The following members are only valid if execute_direct()
 | |
|     or move_to_next_result() returned an error.
 | |
|     They never fail, but if they are called when there is no
 | |
|     result, or no error, the result is not defined.
 | |
|   */
 | |
|   const char *get_last_error() const { return m_diagnostics_area.message(); }
 | |
|   unsigned int get_last_errno() const { return m_diagnostics_area.sql_errno(); }
 | |
|   const char *get_last_sqlstate() const { return m_diagnostics_area.get_sqlstate(); }
 | |
| 
 | |
|   /**
 | |
|     Provided get_field_count() is not 0, this never fails. You don't
 | |
|     need to free the result set, this is done automatically when
 | |
|     you advance to the next result set or destroy the connection.
 | |
|     Not returning const because of List iterator not accepting
 | |
|     Should be used when you would like Ed_connection to manage
 | |
|     result set memory for you.
 | |
|   */
 | |
|   Ed_result_set *use_result_set() { return m_current_rset; }
 | |
|   /**
 | |
|     Provided get_field_count() is not 0, this never fails. You
 | |
|     must free the returned result set. This can be called only
 | |
|     once after execute_direct().
 | |
|     Should be used when you would like to get the results
 | |
|     and destroy the connection.
 | |
|   */
 | |
|   Ed_result_set *store_result_set();
 | |
| 
 | |
|   /**
 | |
|     If the query returns multiple results, this method
 | |
|     can be checked if there is another result beyond the next
 | |
|     one.
 | |
|     Never fails.
 | |
|   */
 | |
|   bool has_next_result() const { return MY_TEST(m_current_rset->m_next_rset); }
 | |
|   /**
 | |
|     Only valid to call if has_next_result() returned true.
 | |
|     Otherwise the result is undefined.
 | |
|   */
 | |
|   bool move_to_next_result()
 | |
|   {
 | |
|     m_current_rset= m_current_rset->m_next_rset;
 | |
|     return MY_TEST(m_current_rset);
 | |
|   }
 | |
| 
 | |
|   ~Ed_connection() { free_old_result(); }
 | |
| private:
 | |
|   Diagnostics_area m_diagnostics_area;
 | |
|   /**
 | |
|     Execute direct interface does not support multi-statements, only
 | |
|     multi-results. So we never have a situation when we have
 | |
|     a mix of result sets and OK or error packets. We either
 | |
|     have a single result set, a single error, or a single OK,
 | |
|     or we have a series of result sets, followed by an OK or error.
 | |
|   */
 | |
|   THD *m_thd;
 | |
|   Ed_result_set *m_rsets;
 | |
|   Ed_result_set *m_current_rset;
 | |
| private:
 | |
|   void free_old_result();
 | |
|   void add_result_set(Ed_result_set *ed_result_set);
 | |
| private:
 | |
|   Ed_connection(const Ed_connection &);        /* not implemented */
 | |
|   Ed_connection &operator=(Ed_connection &);   /* not implemented */
 | |
| };
 | |
| 
 | |
| 
 | |
| /** One result set column. */
 | |
| 
 | |
| struct Ed_column: public LEX_STRING
 | |
| {
 | |
|   /** Implementation note: destructor for this class is never called. */
 | |
| };
 | |
| 
 | |
| 
 | |
| /** One result set record. */
 | |
| 
 | |
| class Ed_row: public Sql_alloc
 | |
| {
 | |
| public:
 | |
|   const Ed_column &operator[](const unsigned int column_index) const
 | |
|   {
 | |
|     return *get_column(column_index);
 | |
|   }
 | |
|   const Ed_column *get_column(const unsigned int column_index) const
 | |
|   {
 | |
|     DBUG_ASSERT(column_index < size());
 | |
|     return m_column_array + column_index;
 | |
|   }
 | |
|   size_t size() const { return m_column_count; }
 | |
| 
 | |
|   Ed_row(Ed_column *column_array_arg, size_t column_count_arg)
 | |
|     :m_column_array(column_array_arg),
 | |
|     m_column_count(column_count_arg)
 | |
|   {}
 | |
| private:
 | |
|   Ed_column *m_column_array;
 | |
|   size_t m_column_count; /* TODO: change to point to metadata */
 | |
| };
 | |
| 
 | |
| extern Atomic_counter<uint32_t> local_connection_thread_count;
 | |
| 
 | |
| #endif // SQL_PREPARE_H
 |