#ifndef SQL_PREPARE_H #define SQL_PREPARE_H /* Copyright (c) 2009, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have either included with the program or referenced in the documentation. 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, version 2.0, 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-1301 USA */ #include #include #include #include #include "lex_string.h" #include "my_alloc.h" #include "my_command.h" #include "my_inttypes.h" #include "my_psi_config.h" #include "mysql/components/services/bits/psi_statement_bits.h" #include "mysql_com.h" #include "sql/sql_class.h" // Query_arena #include "sql/sql_error.h" #include "sql/sql_list.h" #include "sql/statement/statement_runnable.h" class Item; class Item_param; class Prepared_statement; class Query_result_send; class String; struct LEX; struct PS_PARAM; class Table_ref; union COM_DATA; /** 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 final { 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); /** @returns true if some table metadata is changed and statement should be re-prepared. */ bool is_invalidated() const { return m_invalidated; } void reset_reprepare_observer() { m_invalidated = false; } /// @returns true if prepared statement can (and will) be retried bool can_retry() const { // Only call for a statement that is invalidated assert(is_invalidated()); return m_attempt <= MAX_REPREPARE_ATTEMPTS && DBUG_EVALUATE_IF("simulate_max_reprepare_attempts_hit_case", false, true); } private: bool m_invalidated{false}; int m_attempt{0}; /* We take only 3 attempts to reprepare the query, otherwise we might end up in endless loop. */ static constexpr int MAX_REPREPARE_ATTEMPTS = 3; }; void rewrite_query_if_needed(THD *thd); void log_execute_line(THD *thd); bool ask_to_reprepare(THD *thd); bool mysql_stmt_precheck(THD *thd, const COM_DATA *com_data, enum enum_server_command cmd, Prepared_statement **stmt); void mysqld_stmt_prepare(THD *thd, const char *query, uint length, Prepared_statement *stmt); void mysqld_stmt_execute(THD *thd, Prepared_statement *stmt, bool has_new_types, ulong execute_flags, PS_PARAM *parameters); void mysqld_stmt_close(THD *thd, Prepared_statement *stmt); void mysql_sql_stmt_prepare(THD *thd); void mysql_sql_stmt_execute(THD *thd); void mysql_sql_stmt_close(THD *thd); void mysqld_stmt_fetch(THD *thd, Prepared_statement *stmt, ulong num_rows); void mysqld_stmt_reset(THD *thd, Prepared_statement *stmt); void mysql_stmt_get_longdata(THD *thd, Prepared_statement *stmt, uint param_number, uchar *longdata, ulong length); bool select_like_stmt_cmd_test(THD *thd, class Sql_cmd_dml *cmd, ulong setup_tables_done_option); void reset_stmt_parameters(Prepared_statement *stmt); /** Execute direct interface. @todo Implement support for prelocked mode. */ class Server_side_cursor; /** Prepared_statement: a statement that can contain placeholders. */ class Prepared_statement final { public: /// Memory allocation arena, for permanent allocations to statement. Query_arena m_arena; /// Array of parameters used for statement, may be NULL if there are none. Item_param **m_param_array{nullptr}; /// Pointer to cursor, may be NULL if statement never used with a cursor. Server_side_cursor *m_cursor{nullptr}; /// Used to check that the protocol is stable during execution const Protocol *m_active_protocol{nullptr}; /// Number of parameters expected for statement uint m_param_count{0}; uint m_last_errno{0}; char m_last_error[MYSQL_ERRMSG_SIZE]; /** Uniquely identifies each statement object in thread scope; change during statement lifetime. */ const ulong m_id; LEX *m_lex{nullptr}; // parse tree descriptor /// The query string associated with this statement. LEX_CSTRING m_query_string{NULL_CSTR}; /// Performance Schema interface for a prepared statement. PSI_prepared_stmt *m_prepared_stmt{nullptr}; private: /// True if statement is used with cursor, false if used in regular execution bool m_used_as_cursor{false}; /// Query result used when statement is used in regular execution. Query_result *m_regular_result{nullptr}; /// Query result used when statement is used with a cursor. Query_result *m_cursor_result{nullptr}; /// Auxiliary query result object, saved for proper destruction Query_result *m_aux_result{nullptr}; /// Flag that specifies preparation state bool m_is_sql_prepare{false}; /// Flag that prevents recursive invocation of prepared statements bool m_in_use{false}; bool m_with_log{false}; /// Name of the prepared statement. LEX_CSTRING m_name{NULL_CSTR}; /** Name of the current (default) database. If there is the current (default) database, "db" contains its name. If there is no current (default) database, "db" is NULL and "db_length" is 0. In other words, "db", "db_length" must either be NULL, or contain a valid database name. @note this attribute is set and allocated by the slave SQL thread (for the THD of that thread); that thread is (and must remain, for now) the only responsible for freeing this member. */ LEX_CSTRING m_db{NULL_CSTR}; /** The memory root to allocate parsed tree elements (instances of Item, Query_block and other classes). */ MEM_ROOT m_mem_root; bool prepare_query(THD *thd); public: Prepared_statement(THD *thd_arg); virtual ~Prepared_statement(); bool set_name(const LEX_CSTRING &name); const LEX_CSTRING &name() const { return m_name; } ulong id() const { return m_id; } bool is_in_use() const { return m_in_use; } bool is_sql_prepare() const { return m_is_sql_prepare; } void set_sql_prepare(bool prepare = true) { m_is_sql_prepare = prepare; } void deallocate(THD *thd); bool prepare(THD *thd, const char *packet, size_t packet_length, Item_param **orig_param_array); bool execute_loop(THD *thd, String *expanded_query, bool open_cursor); bool execute_server_runnable(THD *thd, Server_runnable *server_runnable); #ifdef HAVE_PSI_PS_INTERFACE PSI_prepared_stmt *get_PS_prepared_stmt() { return m_prepared_stmt; } #endif /** Assign parameter values from the execute packet. @param thd current thread @param expanded_query a container with the original SQL statement. '?' placeholders will be replaced with their values in case of success. The result is used for logging and replication @param has_new_types flag used to signal that new types are provided. @param parameters prepared statement's parsed parameters. @returns false if success, true if error (likely a conversion error, out of memory, or malformed packet) */ bool set_parameters(THD *thd, String *expanded_query, bool has_new_types, PS_PARAM *parameters); enum enum_param_pack_type { /* Parameter values are coming from client over network (vio). */ PACKED, /* Parameter values are sent either using using Server_component service API's or Plugins or directly from SQL layer modules. UNPACKED means that the parameter value buffer points to MYSQL_TIME* */ UNPACKED }; /** Assign parameter values from the execute packet. @param thd current thread @param expanded_query a container with the original SQL statement. '?' placeholders will be replaced with their values in case of success. The result is used for logging and replication @param has_new_types flag used to signal that new types are provided. @param parameters prepared statement's parsed parameters. @param param_pack_type parameters pack type. @returns false if success, true if error (likely a conversion error, out of memory, or malformed packet) */ bool set_parameters(THD *thd, String *expanded_query, bool has_new_types, PS_PARAM *parameters, enum enum_param_pack_type param_pack_type); bool set_parameters(THD *thd, String *expanded_query); void trace_parameter_types(THD *thd); void close_cursor(); private: void cleanup_stmt(THD *thd); void setup_stmt_logging(THD *thd); bool check_parameter_types(); void copy_parameter_types(Item_param **from_param_array); bool set_db(const LEX_CSTRING &db_length); bool execute(THD *thd, String *expanded_query, bool open_cursor); bool reprepare(THD *thd); bool validate_metadata(THD *thd, Prepared_statement *copy); void swap_prepared_statement(Prepared_statement *copy); bool insert_parameters_from_vars(THD *thd, List &varnames, String *query); bool insert_parameters(THD *thd, String *query, bool has_new_types, PS_PARAM *parameters, enum enum_param_pack_type param_pack_type); }; #endif // SQL_PREPARE_H