From 689a284892d336a635869fcbf240c715585da4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Mon, 9 Dec 2019 11:03:47 +0200 Subject: [PATCH] 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. --- include/maxscale/pcre2.hh | 14 +++++++++ server/core/maxscale_pcre2.cc | 23 ++++++++++++++ .../filter/binlogfilter/binlogfilter.cc | 2 +- .../filter/binlogfilter/binlogfilter.hh | 4 +-- .../binlogfilter/binlogfiltersession.cc | 31 ++++++------------- 5 files changed, 49 insertions(+), 25 deletions(-) diff --git a/include/maxscale/pcre2.hh b/include/maxscale/pcre2.hh index f31d2b597..37f42e5d9 100644 --- a/include/maxscale/pcre2.hh +++ b/include/maxscale/pcre2.hh @@ -16,9 +16,23 @@ #include #include +#include + 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.hh * diff --git a/server/core/maxscale_pcre2.cc b/server/core/maxscale_pcre2.cc index 687b24fd2..67030942e 100644 --- a/server/core/maxscale_pcre2.cc +++ b/server/core/maxscale_pcre2.cc @@ -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; +} +} diff --git a/server/modules/filter/binlogfilter/binlogfilter.cc b/server/modules/filter/binlogfilter/binlogfilter.cc index 9d9bdd25b..13ad718eb 100644 --- a/server/modules/filter/binlogfilter/binlogfilter.cc +++ b/server/modules/filter/binlogfilter/binlogfilter.cc @@ -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} } diff --git a/server/modules/filter/binlogfilter/binlogfilter.hh b/server/modules/filter/binlogfilter/binlogfilter.hh index 23fa9c616..2f1ed2feb 100644 --- a/server/modules/filter/binlogfilter/binlogfilter.hh +++ b/server/modules/filter/binlogfilter/binlogfilter.hh @@ -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; }; diff --git a/server/modules/filter/binlogfilter/binlogfiltersession.cc b/server/modules/filter/binlogfilter/binlogfiltersession.cc index 83d13131e..166ab5ccc 100644 --- a/server/modules/filter/binlogfilter/binlogfiltersession.cc +++ b/server/modules/filter/binlogfilter/binlogfiltersession.cc @@ -52,6 +52,7 @@ #include #include #include +#include #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