MXS-2785: Make rewrite_src a regular expression

The use of a regular expression allows multiple rewrite rules to be
combined into one. This allows more versatile conversions but, given the
simple nature of regular expressions, also makes accidental changes more
likely.

Addd mxs::pcre2_substitute that is a more C++-friendly version of
mxs_pcre2_substitute to make. This makes string replacement a lot easier
to do when the source and destination are not C strings.
This commit is contained in:
Markus Mäkelä
2019-12-09 11:03:47 +02:00
parent f3f2748490
commit 689a284892
5 changed files with 49 additions and 25 deletions

View File

@ -16,9 +16,23 @@
#include <maxscale/pcre2.h>
#include <maxscale/utils.hh>
#include <string>
namespace maxscale
{
/**
* Overload that returns a string
*
* @param re Compiled pattern to use
* @param subject Subject string
* @param replace Replacement string
*
* @return The replaced string or the original string if no replacement was made
*/
std::string pcre2_substitute(pcre2_code* re, const std::string& subject, const std::string& replace);
/**
* @class CloserTraits<pcre2_code*> pcre2.hh <maxscale/pcre2.hh>
*

View File

@ -245,3 +245,26 @@ bool mxs_pcre2_check_match_exclude(pcre2_code* re_match,
}
return rval;
}
namespace maxscale
{
std::string pcre2_substitute(pcre2_code* re, const std::string& subject, const std::string& replace)
{
std::string rval = subject;
size_t size_tmp = rval.size();
int rc;
while ((rc = pcre2_substitute(re, (PCRE2_SPTR) subject.c_str(), subject.length(),
0, PCRE2_SUBSTITUTE_GLOBAL, NULL, NULL,
(PCRE2_SPTR) replace.c_str(), replace.length(),
(PCRE2_UCHAR*) &rval[0], &size_tmp)) == PCRE2_ERROR_NOMEMORY)
{
rval.resize(rval.size() * 2);
size_tmp = rval.size();
}
rval.resize(size_tmp);
return rval;
}
}

View File

@ -36,7 +36,7 @@ extern "C" MXS_MODULE* MXS_CREATE_MODULE()
{
{"match", MXS_MODULE_PARAM_REGEX },
{"exclude", MXS_MODULE_PARAM_REGEX },
{REWRITE_SRC, MXS_MODULE_PARAM_STRING },
{REWRITE_SRC, MXS_MODULE_PARAM_REGEX },
{REWRITE_DEST, MXS_MODULE_PARAM_STRING },
{MXS_END_MODULE_PARAMS}
}

View File

@ -29,7 +29,7 @@ struct BinlogConfig
, md_match(match ? pcre2_match_data_create_from_pattern(match, nullptr) : nullptr)
, exclude(pParams->get_compiled_regex("exclude", 0, nullptr).release())
, md_exclude(exclude ? pcre2_match_data_create_from_pattern(exclude, nullptr) : nullptr)
, rewrite_src(pParams->get_string(REWRITE_SRC))
, rewrite_src(pParams->get_compiled_regex(REWRITE_SRC, 0, nullptr).release())
, rewrite_dest(pParams->get_string(REWRITE_DEST))
{
}
@ -38,7 +38,7 @@ struct BinlogConfig
pcre2_match_data* md_match;
pcre2_code* exclude;
pcre2_match_data* md_exclude;
std::string rewrite_src;
pcre2_code* rewrite_src;
std::string rewrite_dest;
};

View File

@ -52,6 +52,7 @@
#include <maxbase/alloc.h>
#include <maxscale/poll.hh>
#include <maxscale/modutil.hh>
#include <maxscale/pcre2.hh>
#include "binlogfilter.hh"
#include "binlogfiltersession.hh"
@ -138,13 +139,12 @@ int BinlogFilterSession::routeQuery(GWBUF* pPacket)
m_state = BINLOG_MODE;
MXS_INFO("Slave server %u is waiting for binlog events.", m_serverid);
if (!m_is_gtid
&& m_filter.getConfig().rewrite_src.length() != m_filter.getConfig().rewrite_dest.length())
if (!m_is_gtid)
{
gwbuf_free(pPacket);
std::ostringstream ss;
ss << "GTID replication is required when '"
<< REWRITE_SRC << "' and '" << REWRITE_DEST << "' are of different length";
<< REWRITE_SRC << "' and '" << REWRITE_DEST << "' are used";
mxs::FilterSession::clientReply(
modutil_create_mysql_err_msg(1, 0, ER_MASTER_FATAL_ERROR_READING_BINLOG, "HY000",
ss.str().c_str()));
@ -849,30 +849,17 @@ void BinlogFilterSession::checkStatement(GWBUF** buffer, const REP_HEADER& hdr)
m_skip = should_skip_query(config, sql, db);
MXS_INFO("[%s] (%s) %s", m_skip ? "SKIP" : " ", db.c_str(), sql.c_str());
if (!m_skip && !config.rewrite_src.empty())
if (!m_skip && config.rewrite_src)
{
bool replace = false;
auto new_db = mxs::pcre2_substitute(config.rewrite_src, db, config.rewrite_dest);
auto new_sql = mxs::pcre2_substitute(config.rewrite_src, sql, config.rewrite_dest);
if (db == config.rewrite_src)
{
replace = true;
db = config.rewrite_dest;
}
size_t pos = 0;
while ((pos = sql.find(config.rewrite_src, pos)) != std::string::npos)
{
replace = true;
sql.replace(pos, config.rewrite_src.length(), config.rewrite_dest);
pos += config.rewrite_dest.length();
}
if (replace)
if (db != new_db || sql != new_sql)
{
db = new_db;
sql = new_sql;
int len = sql.length() + db.length() - statement_len - db_name_len;
if (len > 0)
{
// Buffer is too short, extend it