mysql5/mysql-5.7.27/client/base/mysql_query_runner.cc

483 lines
13 KiB
C++

/*
Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
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-1301 USA
*/
#include "mysql_query_runner.h"
#include "instance_callback.h"
#include "sql_string.h"
#include "m_ctype.h"
using namespace Mysql::Tools::Base;
using Mysql::I_callable;
using Mysql::Instance_callback;
using std::vector;
using std::string;
Mysql_query_runner::Mysql_query_runner(MYSQL* connection)
: m_is_processing(new my_boost::atomic<bool>(false)),
m_is_original_runner(true),
m_connection(connection)
{}
Mysql_query_runner::Mysql_query_runner(const Mysql_query_runner& source)
: m_result_callbacks(source.m_result_callbacks),
m_message_callbacks(source.m_message_callbacks),
m_is_processing(source.m_is_processing),
m_is_original_runner(false),
m_connection(source.m_connection)
{
}
Mysql_query_runner::~Mysql_query_runner()
{
if (m_is_original_runner)
{
for (std::vector<I_callable<int64, const Row&>*>::iterator
it= m_result_callbacks.begin(); it != m_result_callbacks.end(); ++it)
{
delete *it;
}
for (std::vector<I_callable<int64, const Message_data&>*>::iterator
it= m_message_callbacks.begin(); it != m_message_callbacks.end(); ++it)
{
delete *it;
}
delete m_is_processing;
mysql_close(this->m_connection);
}
}
Mysql_query_runner& Mysql_query_runner::add_result_callback(
I_callable<int64, const Row& >* result_callback)
{
m_result_callbacks.push_back(result_callback);
return *this;
}
Mysql_query_runner& Mysql_query_runner::add_message_callback(
I_callable<int64, const Message_data&>* message_callback)
{
m_message_callbacks.push_back(message_callback);
return *this;
}
MYSQL* Mysql_query_runner::get_low_level_connection() const
{
return m_connection;
}
int64 Mysql_query_runner::run_query_store(
std::string query, std::vector<const Row* >* result)
{
Store_result_helper helper(result);
return this->run_query(query, helper.get_result_callback());
}
int64 Mysql_query_runner::run_query(
std::string query,
I_callable<int64, const Row& >* result_callback)
{
Mysql_query_runner copy(*this);
copy.add_result_callback(result_callback);
int64 result= copy.run_query(query);
delete result_callback;
copy.m_result_callbacks.clear();
return result;
}
int64 Mysql_query_runner::run_query(string query)
{
bool expected_value= false;
/*
Try to change processing flag to true. If it's not false already, this
means that another query is in progress, which is not allowed.
*/
if (!m_is_processing->compare_exchange_strong(expected_value, true))
{
Message_data message(1, "Cannot execute more than one "
"MySQL query in parallel on single MySQL connection.",
Message_type_error);
return this->report_message(message);
}
uint64 result= this->run_query_unguarded(query);
*m_is_processing= false;
return result;
}
int64 Mysql_query_runner::run_query_unguarded(string query)
{
if (0 != mysql_query(m_connection, query.c_str()))
return this->report_mysql_error();
MYSQL_RES* results= mysql_use_result(m_connection);
if (results != NULL)
{
for (;;)
{
// Feed result callbacks with results.
MYSQL_ROW row= mysql_fetch_row(results);
if (row == NULL)
{
// NULL row indicates end of rows or error
if (mysql_errno(m_connection) == 0)
break;
else
{
mysql_free_result(results);
return this->report_mysql_error();
}
}
unsigned int columns= mysql_field_count(m_connection);
Row* processed_row= new Row(results, columns, row);
vector<I_callable<int64, const Row& >*>::reverse_iterator it;
for (it= m_result_callbacks.rbegin();
it != m_result_callbacks.rend();
it++)
{
int64 callback_result= (**it)(*processed_row);
if (callback_result != 0)
{
mysql_free_result(results);
return callback_result;
}
}
}
mysql_free_result(results);
}
else
{
if (mysql_errno(m_connection) != 0)
return this->report_mysql_error();
}
// Get all notes, warnings and errors of last query.
if (0 != mysql_query(m_connection, "SHOW WARNINGS") ||
NULL == (results= mysql_use_result(m_connection)))
{
this->report_mysql_error();
return 0;
}
// Process all errors and warnings.
for (;;)
{
// Feed message callbacks with results.
MYSQL_ROW row= mysql_fetch_row(results);
if (row == NULL)
{
// End of rows or an error.
if (mysql_errno(m_connection) != 0)
this->report_mysql_error();
break;
}
unsigned int columns = mysql_field_count(m_connection);
Row* processed_row= new Row(results, columns, row);
Warning_data warning(atoi((*processed_row)[1].c_str()),
(*processed_row)[2],
this->get_message_type_from_severity((*processed_row)[0]));
this->report_message(warning);
delete processed_row;
}
mysql_free_result(results);
return 0;
}
int64 Mysql_query_runner::report_mysql_error()
{
Message_data message(mysql_errno(m_connection), mysql_error(m_connection),
Message_type_error);
return this->report_message(message);
}
int64 Mysql_query_runner::report_message(Message_data &message)
{
vector<I_callable<int64, const Message_data&>*>::reverse_iterator it;
for (it= m_message_callbacks.rbegin();
it != m_message_callbacks.rend();
it++)
{
int64 callback_result= (**it)(message);
if (callback_result < 0)
{
return 0;
}
else if (callback_result != 0)
{
return callback_result;
}
}
return message.get_code();
}
enum Message_type Mysql_query_runner::get_message_type_from_severity(
string severity)
{
for (int i= 0; i+1 < Message_data::message_type_strings_count; i++)
{
String severity_string;
uint dummy_errors;
severity_string.copy(Message_data::message_type_strings[i],
strlen(Message_data::message_type_strings[i]),
&my_charset_latin1, m_connection->charset, &dummy_errors);
if (my_strcasecmp(m_connection->charset, severity.c_str(),
severity_string.c_ptr_safe()) == 0)
{
return (Message_type)i;
}
}
return Message_type_unknown;
}
Mysql_query_runner::Store_result_helper::Store_result_helper(
std::vector<const Row*>* result)
: m_result(result)
{}
I_callable<int64, const Mysql_query_runner::Row&>*
Mysql_query_runner::Store_result_helper::get_result_callback()
{
return new Instance_callback<
int64, const Row&, Store_result_helper>(
this, &Store_result_helper::result_callback);
}
int64 Mysql_query_runner::Store_result_helper::result_callback(const Row& row)
{
m_result->push_back(&row);
return 0;
}
void Mysql_query_runner::cleanup_result(const Mysql_query_runner::Row& result)
{
delete &result;
}
std::string Mysql_query_runner::escape_string(const std::string& original)
{
std::string res;
this->append_escape_string(&res, original);
return res;
}
void Mysql_query_runner::append_escape_string(
std::string* destination_string, const std::string& original)
{
this->append_escape_string(
destination_string, original.c_str(), original.size());
}
void Mysql_query_runner::append_escape_string(
std::string* destination_string, const char* original, size_t original_length)
{
size_t start_lenght= destination_string->size();
size_t required_capacity= start_lenght + original_length * 2 + 1;
destination_string->resize(required_capacity);
int length = mysql_real_escape_string_quote(
m_connection, &((*destination_string)[0]) + start_lenght, original,
(ulong)original_length, '"');
destination_string->resize(start_lenght + length);
}
void Mysql_query_runner::append_hex_string(
std::string* destination_string, const char* original, size_t original_length)
{
size_t start_lenght= destination_string->size();
size_t required_capacity= start_lenght + original_length * 2 + 1;
destination_string->resize(required_capacity);
int length = mysql_hex_string(
&((*destination_string)[0]) + start_lenght, original,
original_length);
destination_string->resize(start_lenght + length);
}
void Mysql_query_runner::cleanup_result(
std::vector<const Mysql_query_runner::Row*>* result)
{
for (vector<const Mysql_query_runner::Row*>::const_iterator
it= result->begin(); it != result->end(); it++)
{
delete *it;
}
result->clear();
}
Mysql_query_runner::Row::Row(MYSQL_RES* mysql_result_info, unsigned int column_count,
MYSQL_ROW row)
: m_buffer(NULL),
m_buffer_capacity(0),
m_buffer_size(0),
m_mysql_result_info(mysql_result_info)
{
size_t total_length= 0;
unsigned long* column_lengths= mysql_fetch_lengths(mysql_result_info);
for (unsigned int column = 0; column < column_count; column++)
total_length+= column_lengths[column] + 1;
this->reserve(column_count, total_length);
// first column always starts at 0 offset
m_buffer_starts.push_back(0);
for (unsigned int column = 0; column < column_count; column++)
{
this->push_back(row[column], column_lengths[column]);
}
}
Mysql_query_runner::Row::~Row()
{
if (m_buffer != NULL)
free(m_buffer);
}
std::string Mysql_query_runner::Row::operator[] (std::size_t index) const
{
std::size_t length;
const char* buffer= this->get_buffer(index, length);
return std::string(buffer, length);
}
void Mysql_query_runner::Row::push_back(char* buff, std::size_t length)
{
if (buff != NULL)
{
// Prepare buffer to be able to contain new data.
this->reserve(
m_buffer_starts.size(), m_buffer_size + length + 1);
// Copy data.
memcpy(&m_buffer[m_buffer_size], buff, length);
// Set new buffer length.
m_buffer_size += length + 1;
// Set sentinel NULL terminating character.
m_buffer[m_buffer_size - 1]= 0;
// Add new end marker.
m_buffer_starts.push_back(m_buffer_size);
}
else
{
// Prepare to be able to contain new data NULL value marker.
this->reserve(m_buffer_starts.size(), m_buffer_size);
// Add new end marker specifying NULL value.
m_buffer_starts.push_back(SIZE_T_MAX);
}
}
const char* Mysql_query_runner::Row::get_buffer(
std::size_t index, std::size_t& length) const
{
std::size_t start= SIZE_T_MAX;
for (std::size_t start_index= index; start == SIZE_T_MAX; start_index--)
{
start= m_buffer_starts[start_index];
}
std::size_t end= m_buffer_starts[index + 1];
length = (end - start - 1);
return &m_buffer[start];
}
std::size_t Mysql_query_runner::Row::size_of_element(std::size_t index) const
{
std::size_t res= 0;
if (!is_value_null(index))
this->get_buffer(index, res);
return res;
}
bool Mysql_query_runner::Row::is_value_null(std::size_t index) const
{
return m_buffer_starts[index + 1] == SIZE_T_MAX;
}
std::size_t Mysql_query_runner::Row::size() const
{
return m_buffer_starts.size() - 1;
}
void Mysql_query_runner::Row::reserve(
std::size_t strings, std::size_t buffer_size)
{
if (strings >= m_buffer_starts.capacity())
{
m_buffer_starts.reserve(strings + 1);
}
if (buffer_size > m_buffer_capacity)
{
m_buffer_capacity= buffer_size;
m_buffer= (char*)realloc(m_buffer, m_buffer_capacity);
}
}
Mysql_query_runner::Row::Iterator Mysql_query_runner::Row::begin() const
{
return Mysql_query_runner::Row::Iterator(*this, 0);
}
Mysql_query_runner::Row::Iterator Mysql_query_runner::Row::end() const
{
return Mysql_query_runner::Row::Iterator(*this, m_buffer_starts.size() - 1);
}
MYSQL_RES* Mysql_query_runner::Row::get_mysql_result_info() const
{
return m_mysql_result_info;
}
Mysql_query_runner::Row::Iterator::Iterator(const Row& row, std::size_t index)
: m_row(row),
m_index(index)
{}
bool Mysql_query_runner::Row::Iterator::is_value_null()
{
return m_row.is_value_null(m_index);
}
std::string Mysql_query_runner::Row::Iterator::operator *()
{
return m_row[m_index];
}
void Mysql_query_runner::Row::Iterator::operator ++()
{
m_index++;
}
bool Mysql_query_runner::Row::Iterator::operator ==(const Iterator& other)
{
return &m_row == &other.m_row && m_index == other.m_index;
}
bool Mysql_query_runner::Row::Iterator::operator !=(const Iterator& other)
{
return !(*this == other);
}