237 lines
6.4 KiB
C++

/* Copyright (c) 2015, 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 "my_config.h"
#include "messages.h"
#include "persisted_rule.h"
#include "rewriter.h"
#include "rule.h"
#include "nullable.h"
#include <mysql/service_parser.h>
#include <mysql/service_rules_table.h>
#include <hash.h>
#include "template_utils.h"
#include <m_string.h> // Needed because debug_sync.h is not self-sufficient.
#include "debug_sync.h"
#include <memory>
#include <string>
using std::string;
using rules_table_service::Cursor;
using Mysql::Nullable;
namespace messages = rewriter_messages;
/**
@file rewriter.cc
Implementation of the Rewriter class's member functions.
*/
/** Functions used in the hash */
uchar *get_rule_hash_code(const uchar *entry, size_t *length,
my_bool MY_ATTRIBUTE((unused)))
{
const Rule *rule= pointer_cast<const Rule*>(entry);
*length= PARSER_SERVICE_DIGEST_LENGTH;
const uchar *digest= pointer_cast<const uchar*>(rule->digest_buffer());
/*
What on earth is the hash table going to do with a non-const hash key?
Maybe I don't want to know...
*/
return const_cast<uchar*>(digest);
}
void free_rule(void *entry) { delete pointer_cast<Rule*>(entry); }
Rewriter::Rewriter()
{
my_hash_init(&m_digests, &my_charset_bin, 10, 0,
PARSER_SERVICE_DIGEST_LENGTH,
get_rule_hash_code,
free_rule, 0,
PSI_INSTRUMENT_ME);
}
Rewriter::~Rewriter() { my_hash_free(&m_digests); }
bool Rewriter::load_rule(MYSQL_THD thd, Persisted_rule *diskrule)
{
std::auto_ptr<Rule> memrule_ptr(new Rule);
Rule *memrule= memrule_ptr.get();
Rule::Load_status load_status= memrule->load(thd, diskrule);
switch (load_status)
{
case Rule::OK:
my_hash_insert(&m_digests, pointer_cast<uchar*>(memrule_ptr.release()));
diskrule->message= Nullable<string>();
diskrule->pattern_digest= services::print_digest(memrule->digest_buffer());
diskrule->normalized_pattern= memrule->normalized_pattern();
return false;
break;
case Rule::PATTERN_GOT_NO_DIGEST:
diskrule->set_message(messages::PATTERN_GOT_NO_DIGEST);
break;
case Rule::PATTERN_PARSE_ERROR:
diskrule->set_message(string(messages::PATTERN_PARSE_ERROR) + ": "
">>" +
memrule->pattern_parse_error_message() +
"<<");
break;
case Rule::PATTERN_NOT_A_SELECT_STATEMENT:
diskrule->set_message(messages::PATTERN_NOT_A_SELECT_STATEMENT);
break;
case Rule::REPLACEMENT_PARSE_ERROR:
diskrule->set_message(string(messages::REPLACEMENT_PARSE_ERROR) + ": "
">>" +
memrule->replacement_parse_error_message() +
"<<");
break;
case Rule::REPLACEMENT_HAS_MORE_MARKERS:
diskrule->set_message(messages::REPLACEMENT_HAS_MORE_MARKERS);
break;
}
return true;
}
#ifndef DBUG_OFF
/**
Normal debug sync points will not work in the THD that the plugin creates,
so we have to call the debug sync functions ourselves.
*/
static void do_debug_sync(MYSQL_THD thd)
{
DBUG_ASSERT(opt_debug_sync_timeout > 0);
const char act[]= "now signal parked wait_for go";
DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(act)));
}
#endif
void Rewriter::do_refresh(MYSQL_THD session_thd)
{
bool saw_rule_error= false;
DBUG_ENTER("Rewriter::do_refresh");
Cursor c(session_thd);
DBUG_PRINT("info", ("Rewriter::do_refresh cursor opened"));
DBUG_EXECUTE_IF("dbug.block_do_refresh", { do_debug_sync(session_thd); });
if (c.table_is_malformed())
{
m_refresh_status= REWRITER_ERROR_TABLE_MALFORMED;
DBUG_VOID_RETURN;
}
my_hash_reset(&m_digests);
for (; c != rules_table_service::end(); ++c)
{
Persisted_rule diskrule(&c);
if (diskrule.is_enabled)
{
if (!diskrule.pattern.has_value())
{
diskrule.set_message("Pattern is NULL.");
saw_rule_error= true;
}
else if (!diskrule.replacement.has_value())
{
diskrule.set_message("Replacement is NULL.");
saw_rule_error= true;
}
else
saw_rule_error|= load_rule(session_thd, &diskrule);
diskrule.write_to(&c);
}
}
if (c.had_serious_read_error())
m_refresh_status= REWRITER_ERROR_READ_FAILED;
else if (saw_rule_error)
m_refresh_status= REWRITER_ERROR_LOAD_FAILED;
else
m_refresh_status= REWRITER_OK;
DBUG_VOID_RETURN;
}
namespace {
struct Refresh_callback_args
{
Rewriter *me;
MYSQL_THD session_thd;
};
extern "C"
void *refresh_callback(void *p_args)
{
Refresh_callback_args *args= pointer_cast<Refresh_callback_args*>(p_args);
(args->me->do_refresh)(args->session_thd);
return NULL;
}
} // namespace
Rewriter::Load_status Rewriter::refresh(MYSQL_THD thd)
{
services::Session session(thd);
Refresh_callback_args args= { this, session.thd() };
m_refresh_status= REWRITER_OK;
my_thread_handle handle;
mysql_parser_start_thread(session.thd(), refresh_callback, &args, &handle);
mysql_parser_join_thread(&handle);
return m_refresh_status;
}
Rewrite_result Rewriter::rewrite_query(MYSQL_THD thd, const uchar *key)
{
HASH_SEARCH_STATE state;
Rewrite_result result;
Rule *rule= pointer_cast<Rule*>(my_hash_first(&m_digests, key,
PARSER_SERVICE_DIGEST_LENGTH,
&state));
while (rule != NULL)
{
result.digest_matched= true;
if (rule->matches(thd))
{
result= rule->create_new_query(thd);
if (result.was_rewritten)
return result;
}
rule= pointer_cast<Rule*>(my_hash_next(&m_digests, key,
PARSER_SERVICE_DIGEST_LENGTH,
&state));
}
result.was_rewritten= false;
return result;
}