630 lines
17 KiB
C++
630 lines
17 KiB
C++
/* Copyright (c) 2023, 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 */
|
|
|
|
#ifndef RUN_COMMAND_H
|
|
#define RUN_COMMAND_H
|
|
|
|
#include <cstddef>
|
|
#include <forward_list>
|
|
#include <new>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
#include "lex_string.h"
|
|
#include "my_alloc.h"
|
|
#include "my_inttypes.h"
|
|
#include "mysql_time.h"
|
|
#include "sql/current_thd.h"
|
|
#include "sql/sql_class.h"
|
|
#include "sql/sql_cursor.h"
|
|
#include "sql/sql_prepare.h" // Prepared_statement
|
|
#include "sql/statement/protocol_local_v2.h"
|
|
#include "sql/statement/statement_runnable.h"
|
|
#include "utils.h"
|
|
|
|
/**
|
|
There must be one function of this kind in order for the symbols in the
|
|
server's dynamic library to be visible to components.
|
|
*/
|
|
int dummy_function_to_ensure_we_are_linked_into_the_server();
|
|
|
|
/**
|
|
* @brief
|
|
* Statement_handle is extension of Ed_connection. Some of the
|
|
* limitations in Ed_connection is that,
|
|
* - It does not support reading result metadata
|
|
* - It does not support prepared statement, parameters and cursors.
|
|
*
|
|
* Above limitation leads to implementation of this interface.
|
|
*
|
|
* Statement_handle supports both execution of regular and
|
|
* prepared statement.
|
|
*
|
|
* Note that we can get rid of Ed_connection implementation
|
|
* as we support more functionality with Statement_handle.
|
|
*/
|
|
class Statement_handle {
|
|
public:
|
|
Statement_handle(THD *thd, const char *query, size_t length);
|
|
|
|
/**
|
|
* @brief Check if error is reported.
|
|
*
|
|
* @return true of error is reported.
|
|
*/
|
|
bool is_error() const { return m_diagnostics_area->is_error(); }
|
|
|
|
/**
|
|
* @brief Check if error is reported.
|
|
*
|
|
* @return true of error is reported.
|
|
*/
|
|
const char *get_last_error() {
|
|
assert(is_error());
|
|
return convert_and_store(&m_warning_mem_root,
|
|
m_diagnostics_area->message_text(),
|
|
strlen(m_diagnostics_area->message_text()),
|
|
system_charset_info, m_expected_charset);
|
|
}
|
|
|
|
/**
|
|
* @brief Get the last mysql errno.
|
|
*
|
|
* @return unsigned int
|
|
*/
|
|
unsigned int get_last_errno() const {
|
|
assert(is_error());
|
|
return m_diagnostics_area->mysql_errno();
|
|
}
|
|
|
|
/**
|
|
* @brief Get the mysql error state.
|
|
*
|
|
* @return const char*
|
|
*/
|
|
const char *get_mysql_state() {
|
|
assert(is_error());
|
|
return convert_and_store(&m_warning_mem_root,
|
|
m_diagnostics_area->returned_sqlstate(),
|
|
strlen(m_diagnostics_area->returned_sqlstate()),
|
|
system_charset_info, m_expected_charset);
|
|
}
|
|
|
|
/**
|
|
* @brief Get number of warnings generated.
|
|
*
|
|
* @return ulonglong
|
|
*/
|
|
ulonglong warning_count() const { return m_warnings_count; }
|
|
|
|
/**
|
|
* @brief Get list of all warnings.
|
|
*
|
|
* @return Warning*
|
|
*/
|
|
Warning *get_warnings();
|
|
|
|
/**
|
|
* @brief Get the next result sets generated while executing the statement.
|
|
*
|
|
* @return Result_set*
|
|
*/
|
|
Result_set *get_result_sets() { return m_result_sets; }
|
|
|
|
/**
|
|
* @brief Get the current result set object
|
|
*
|
|
* @return Result_set*
|
|
*/
|
|
Result_set *get_current_result_set() { return m_current_rset; }
|
|
|
|
/**
|
|
* @brief Make the next result set the current result set.
|
|
* We do it once we have read the current result set.
|
|
*
|
|
*/
|
|
void next_result_set() {
|
|
auto next_rset = m_current_rset->get_next();
|
|
m_current_rset = next_rset;
|
|
}
|
|
|
|
/**
|
|
* @brief Execute the SQL command.
|
|
* This can be either Regular or Prepared statement.
|
|
*
|
|
* @return true upon failure.
|
|
* @return false upon success.
|
|
*/
|
|
virtual bool execute() = 0;
|
|
|
|
/**
|
|
* @brief Check if we are processing a prepared statement.
|
|
*
|
|
* @return true upon failure.
|
|
* @return false upon success.
|
|
*/
|
|
virtual bool is_prepared_statement() = 0;
|
|
|
|
/**
|
|
* @brief Check if the statement has been executed or prepared
|
|
*
|
|
* @return true upon executed or prepared
|
|
* @return false otherwise
|
|
*/
|
|
virtual bool is_executed_or_prepared() = 0;
|
|
|
|
/**
|
|
* @brief Feel all the result collected so far, from query execution.
|
|
*
|
|
*/
|
|
void free_old_result();
|
|
|
|
/**
|
|
* @brief Get the query string being executed.
|
|
*
|
|
* @return std::string_view
|
|
*/
|
|
std::string_view get_query() { return m_query; }
|
|
|
|
/**
|
|
* @brief Set the capacity in bytes allowed for caching results.
|
|
*
|
|
* @param capacity of the result set
|
|
*/
|
|
void set_capacity(size_t capacity) {
|
|
m_protocol.set_result_set_capacity(capacity);
|
|
}
|
|
|
|
/**
|
|
* @brief Get the capacity in bytes allowed for caching results.
|
|
*
|
|
* @return size_t
|
|
*/
|
|
size_t get_capacity() { return m_protocol.get_result_set_capacity(); }
|
|
|
|
/**
|
|
* @brief Set thd protocol to enable result pass through.
|
|
*
|
|
* @param use_thd_protocol - parameter that decides if THD protocol should be
|
|
* used
|
|
*/
|
|
void set_use_thd_protocol(bool use_thd_protocol) {
|
|
m_use_thd_protocol = use_thd_protocol;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if thd protocol is used.
|
|
*
|
|
* @return true if enabled.
|
|
* @return false if not.
|
|
*/
|
|
bool is_using_thd_protocol() const { return m_use_thd_protocol; }
|
|
|
|
/**
|
|
* @brief Set either Protocol_local_v2 when m_use_thd_protocol is not set or
|
|
* or classical protocol when m_use_thd_protocol is set to THD.
|
|
*/
|
|
void set_thd_protocol();
|
|
|
|
/**
|
|
* @brief Reset THD protocol.
|
|
*/
|
|
void reset_thd_protocol();
|
|
|
|
/**
|
|
* @brief Set the expected charset
|
|
*
|
|
* @param charset_name Name of the charset
|
|
*/
|
|
void set_expected_charset(const char *charset_name) {
|
|
m_expected_charset =
|
|
get_charset_by_csname(charset_name, MY_CS_PRIMARY, MYF(0));
|
|
}
|
|
|
|
/**
|
|
* @brief Get the expected charset
|
|
*
|
|
* @return const char* the expected charset name
|
|
*/
|
|
const char *get_expected_charset() {
|
|
if (m_expected_charset == nullptr) return nullptr;
|
|
return m_expected_charset->csname;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the num rows per fetch.
|
|
*
|
|
* @return size_t
|
|
*/
|
|
size_t get_num_rows_per_fetch() { return m_num_rows_per_fetch; }
|
|
|
|
/**
|
|
* @brief Set the num of rows to be retrieved per fetch.
|
|
*
|
|
* @param num_rows_per_fetch number of rows per fetch
|
|
*/
|
|
void set_num_rows_per_fetch(size_t num_rows_per_fetch) {
|
|
m_num_rows_per_fetch = num_rows_per_fetch;
|
|
}
|
|
|
|
/**
|
|
* @brief Destroy the Statement_handle object
|
|
*
|
|
*/
|
|
virtual ~Statement_handle() { free_old_result(); }
|
|
|
|
protected:
|
|
/**
|
|
* @brief Send statement execution status after execute().
|
|
*/
|
|
void send_statement_status();
|
|
|
|
protected:
|
|
// The query being executed.
|
|
std::string m_query;
|
|
|
|
// Details about error or warning occured.
|
|
MEM_ROOT m_warning_mem_root;
|
|
Warning *m_warnings;
|
|
ulonglong m_warnings_count = 0;
|
|
Diagnostics_area *m_diagnostics_area;
|
|
|
|
// The thread executing the statement.
|
|
THD *m_thd;
|
|
|
|
// List of all results generated by the query.
|
|
Result_set *m_result_sets;
|
|
|
|
// The last result generated by the guery.
|
|
Result_set *m_current_rset;
|
|
|
|
// Do not intercept the results in the protocol, but pass it to
|
|
// THD* protocol. E.g., if this is set to 'false', the results are
|
|
// captured into Protocol_result_v2, else it goes to THD->get_protocol()
|
|
bool m_use_thd_protocol = false;
|
|
|
|
// Max rows to read per fetch() call.
|
|
size_t m_num_rows_per_fetch = 1;
|
|
|
|
CHARSET_INFO *m_expected_charset;
|
|
|
|
// Collect result set from single statement.
|
|
void add_result_set(Result_set *result_set);
|
|
|
|
// Set result set from prepared statement with cursor.
|
|
// This is called at the end of fetch().
|
|
void set_result_set(Result_set *result_set);
|
|
|
|
/**
|
|
* @brief Copy the warnings generated for the query from the
|
|
* diagnostics area
|
|
*/
|
|
void copy_warnings();
|
|
|
|
// Protocol that intercepts all the rows from query executed.
|
|
Protocol_local_v2 m_protocol;
|
|
friend class Protocol_local_v2;
|
|
|
|
// Protocol used by THD before switching.
|
|
Protocol *m_saved_protocol{nullptr};
|
|
|
|
// No copies.
|
|
Statement_handle(const Statement_handle &) = delete;
|
|
// No move.
|
|
Statement_handle(Statement_handle &&) = delete;
|
|
// No self assignment.
|
|
Statement_handle &operator=(const Statement_handle &) = delete;
|
|
// No move.
|
|
Statement_handle &operator=(Statement_handle &&) = delete;
|
|
};
|
|
|
|
/**
|
|
* @brief Regular_statement_handle enables execution of all SQL statements
|
|
* except for prepared statements.
|
|
*/
|
|
class Regular_statement_handle : public Statement_handle {
|
|
public:
|
|
Regular_statement_handle(THD *thd, const char *query, uint length)
|
|
: Statement_handle(thd, query, length) {}
|
|
|
|
/**
|
|
* @brief Execute a regular statement.
|
|
*
|
|
* @return true if statement fails.
|
|
* @return false if statement succeeds.
|
|
*/
|
|
bool execute() override;
|
|
|
|
/**
|
|
* @brief Convey that this is regular statement.
|
|
*
|
|
* @return Always returns 'false'
|
|
*/
|
|
bool is_prepared_statement() override { return false; }
|
|
|
|
/**
|
|
* @brief Check if the statement has been executed
|
|
*
|
|
* @return true if executed
|
|
* @return false otherwise
|
|
*/
|
|
bool is_executed_or_prepared() override { return m_is_executed; }
|
|
|
|
private:
|
|
// Flag to show whether statement has been executed. Change to true after
|
|
// execute is called
|
|
bool m_is_executed = false;
|
|
|
|
// Used by execute() above.
|
|
bool execute(Server_runnable *server_runnable);
|
|
};
|
|
|
|
/**
|
|
* @brief Prepared_statement_handle enables support for prepared
|
|
* statement execution. Supports parameters and cursors.
|
|
*/
|
|
class Prepared_statement_handle : public Statement_handle {
|
|
public:
|
|
Prepared_statement_handle(THD *thd, const char *query, uint length)
|
|
: Statement_handle(thd, query, length),
|
|
m_parameter_mem_root(key_memory_prepared_statement_main_mem_root,
|
|
thd->variables.query_alloc_block_size) {}
|
|
|
|
/**
|
|
* @brief Prepares the statement using m_query.
|
|
*
|
|
* If the statement is already in executing mode, close the cursor
|
|
* deallocate the previous statement and start preparing new statement
|
|
* using m_query.
|
|
*
|
|
* @return true if fails.
|
|
* @return false if succeeds.
|
|
*/
|
|
bool prepare();
|
|
|
|
/**
|
|
* @brief Set the parameter value in a prepared statement.
|
|
*
|
|
* @param idx Index of '?' in prepared statement.
|
|
* @param is_null Set parameter to NULL value.
|
|
* @param type Set the parameters field type.
|
|
* @param is_unsigned Mark parameter as unsigned.
|
|
* @param data Pointer to buffer containing the value.
|
|
* @param data_length Length of buffer 'data'
|
|
* @param name Name of parameter (mostly unused)
|
|
* @param name_length Length of 'name'
|
|
*
|
|
* @return true if fails.
|
|
* @return false if succeeds.
|
|
*/
|
|
bool set_parameter(uint idx, bool is_null, enum_field_types type,
|
|
bool is_unsigned, const void *data,
|
|
unsigned long data_length, const char *name,
|
|
unsigned long name_length);
|
|
|
|
/**
|
|
* @brief Get the parameter object
|
|
*
|
|
* @param index of the parameter
|
|
* @return Item_param*
|
|
*/
|
|
Item_param *get_parameter(size_t index);
|
|
|
|
/**
|
|
* @brief Execute the statement that is prepared.
|
|
*
|
|
* If a statement is not yet prepared, we fail.
|
|
*
|
|
* If a statement was already in EXECUTED state, we close the cursor
|
|
* and execute the statement again.
|
|
*
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool execute() override;
|
|
|
|
/**
|
|
* @brief Fetch rows from statement using cursor.
|
|
*
|
|
* Not all statement uses cursor. Check is_cursor_open() and
|
|
* then invoke this call.
|
|
*
|
|
* Attempt to call fetch rows without preparing or executing the
|
|
* statement will cause failure.
|
|
*
|
|
* Attempt to call fetch() without cursor in use, will cause failure.
|
|
*
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool fetch();
|
|
|
|
/**
|
|
* @brief Check if the statement uses cursor and it is open.
|
|
*
|
|
* If the API is called without preparing the statement will
|
|
* result in 'false'
|
|
*
|
|
* @return true if cursor is in use
|
|
* @return false if cursor not in use.
|
|
*/
|
|
bool is_cursor_open() {
|
|
if (!m_stmt) return false;
|
|
return m_stmt->m_cursor && m_stmt->m_cursor->is_open();
|
|
}
|
|
|
|
/**
|
|
* @brief Check if the statement uses cursor.
|
|
*
|
|
* If the API is called without executing the statement
|
|
* will result in 'false'
|
|
*
|
|
* @return true if statement uses cursor.
|
|
* @return false if statement does not uses cursor.
|
|
*/
|
|
bool uses_cursor() {
|
|
if (!m_stmt || m_stmt->m_arena.get_state() != Query_arena::STMT_EXECUTED)
|
|
return false;
|
|
return m_stmt->m_cursor != nullptr;
|
|
}
|
|
|
|
/**
|
|
* @brief Reset the statement parameters and cursors.
|
|
*
|
|
* Invoking this API will close the cursor in use.
|
|
* This is invoked generally before executing
|
|
* the statement for the second time, after prepare.
|
|
*
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool reset();
|
|
|
|
/**
|
|
* @brief Close the statement that is prepared.
|
|
*
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool close();
|
|
|
|
/**
|
|
* @brief Get number of parameters used in the statement.
|
|
*
|
|
* This should be called after preparing a statement.
|
|
*
|
|
* @return uint
|
|
*/
|
|
uint get_param_count() { return m_stmt ? m_stmt->m_param_count : 0; }
|
|
|
|
/**
|
|
* @brief Convey that this is prepared statement.
|
|
*
|
|
* @return Always returns true
|
|
*/
|
|
bool is_prepared_statement() override { return true; }
|
|
|
|
/**
|
|
* @brief Check if the statement has been executed or prepared
|
|
*
|
|
* @return true if executed or prepared
|
|
* @return false otherwise
|
|
*/
|
|
bool is_executed_or_prepared() override {
|
|
return m_stmt != nullptr &&
|
|
m_stmt->m_arena.get_state() > Query_arena::STMT_INITIALIZED;
|
|
}
|
|
|
|
/**
|
|
* @brief Virtual destroy for Prepared_statement_handle object
|
|
*
|
|
*/
|
|
virtual ~Prepared_statement_handle() override { internal_close(); }
|
|
|
|
private:
|
|
/**
|
|
* @brief This is a wrapper function used to execute
|
|
* and operation on Prepared_statement. This takes case of using
|
|
* relevant protocol, diagnostic area, backup the current query arena
|
|
* before executing the prepared statement operation.
|
|
*
|
|
* @tparam Function type of function to run
|
|
* @param exec_func function to run
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
template <typename Function>
|
|
bool run(Function exec_func);
|
|
|
|
// See prepare()
|
|
bool internal_prepare();
|
|
|
|
// See execute()
|
|
bool internal_execute();
|
|
|
|
// See fetch()
|
|
bool internal_fetch();
|
|
|
|
// See reset()
|
|
bool internal_reset();
|
|
|
|
/**
|
|
* @brief Reset the statement parameters and cursors.
|
|
*
|
|
* @param invalidate_params When set to true parameters are invalidated.
|
|
*
|
|
* @return true on failure.
|
|
* @return false on execute.
|
|
*/
|
|
|
|
bool internal_reset(bool invalidate_params);
|
|
|
|
// See close()
|
|
bool internal_close();
|
|
|
|
/**
|
|
* @brief Method to enable cursor.
|
|
*
|
|
* @return true if cursor can be used for a statement.
|
|
* @return false if cursor can not be used for a statement.
|
|
*/
|
|
bool enable_cursor();
|
|
|
|
/**
|
|
* @brief Create a parameter buffers
|
|
*
|
|
* @return true on failure
|
|
* @return false on success
|
|
*/
|
|
bool create_parameter_buffers();
|
|
|
|
// The prepared statement implementation.
|
|
Prepared_statement *m_stmt{nullptr};
|
|
|
|
// Parameters values.
|
|
PS_PARAM *m_parameters{nullptr};
|
|
|
|
/*
|
|
Store the parameter and parameter values in a separate mem_root.
|
|
These can be even stored in m_stmt's mem_root. But reprepare() would
|
|
free up the memory used by the prepared statement. Hence, using separate
|
|
mem_root.
|
|
*/
|
|
MEM_ROOT m_parameter_mem_root;
|
|
|
|
/*
|
|
Size of each parameter buffer allocated in mem_root.
|
|
We re-use the same buffer, if the parameter value size fit
|
|
within this max.
|
|
*/
|
|
ulong *m_parameter_buffer_max{nullptr};
|
|
|
|
/*
|
|
Denotes if new parameters were set.
|
|
This is set 'true' for the first time. It is set to 'false' upon
|
|
re-execute without rebinding any parameters.
|
|
*/
|
|
bool m_bound_new_parameter_types{true};
|
|
};
|
|
|
|
#endif
|