diff options
Diffstat (limited to 'sql/sql_prepare.h')
-rw-r--r-- | sql/sql_prepare.h | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h new file mode 100644 index 00000000..ff6e986e --- /dev/null +++ b/sql/sql_prepare.h @@ -0,0 +1,358 @@ +#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; } +private: + bool m_invalidated; +}; + + +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 |