MXS-701: binlog filtering
MXS-701: first implementation of binlog filter
This commit is contained in:
@ -14,3 +14,4 @@ add_subdirectory(topfilter)
|
|||||||
add_subdirectory(tpmfilter)
|
add_subdirectory(tpmfilter)
|
||||||
add_subdirectory(masking)
|
add_subdirectory(masking)
|
||||||
add_subdirectory(insertstream)
|
add_subdirectory(insertstream)
|
||||||
|
add_subdirectory(binlogfilter)
|
||||||
|
|||||||
3
server/modules/filter/binlogfilter/CMakeLists.txt
Normal file
3
server/modules/filter/binlogfilter/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
add_library(binlogfilter SHARED binlogfilter.cc binlogfiltersession.cc)
|
||||||
|
set_target_properties(binlogfilter PROPERTIES VERSION "1.0.0")
|
||||||
|
install_module(binlogfilter core)
|
||||||
92
server/modules/filter/binlogfilter/binlogfilter.cc
Normal file
92
server/modules/filter/binlogfilter/binlogfilter.cc
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// All log messages from this module are prefixed with this
|
||||||
|
#define MXS_MODULE_NAME "binlogfilter"
|
||||||
|
|
||||||
|
#include "binlogfilter.hh"
|
||||||
|
|
||||||
|
// This declares a module in MaxScale
|
||||||
|
extern "C" MXS_MODULE* MXS_CREATE_MODULE()
|
||||||
|
{
|
||||||
|
static MXS_MODULE info =
|
||||||
|
{
|
||||||
|
MXS_MODULE_API_FILTER,
|
||||||
|
MXS_MODULE_IN_DEVELOPMENT,
|
||||||
|
MXS_FILTER_VERSION,
|
||||||
|
"A binlog event filter for slave servers",
|
||||||
|
"V1.0.0",
|
||||||
|
RCAP_TYPE_NONE,
|
||||||
|
&BinlogFilter::s_object, // This is defined in the MaxScale filter template
|
||||||
|
NULL, /* Process init. */
|
||||||
|
NULL, /* Process finish. */
|
||||||
|
NULL, /* Thread init. */
|
||||||
|
NULL, /* Thread finish. */
|
||||||
|
{
|
||||||
|
{"filter_events", MXS_MODULE_PARAM_BOOL, "false"},
|
||||||
|
{"skip_table", MXS_MODULE_PARAM_STRING, ""},
|
||||||
|
{"skip_db", MXS_MODULE_PARAM_STRING, ""},
|
||||||
|
{ MXS_END_MODULE_PARAMS }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return &info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinlogFilter constructor
|
||||||
|
BinlogFilter::BinlogFilter(const MXS_CONFIG_PARAMETER* pParams)
|
||||||
|
: m_config(pParams)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinlogFilter destructor
|
||||||
|
BinlogFilter::~BinlogFilter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// static: filter create routine
|
||||||
|
BinlogFilter* BinlogFilter::create(const char* zName,
|
||||||
|
char** pzOptions,
|
||||||
|
MXS_CONFIG_PARAMETER* pParams)
|
||||||
|
{
|
||||||
|
return new BinlogFilter(pParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinlogFilterSession create routine
|
||||||
|
BinlogFilterSession* BinlogFilter::newSession(MXS_SESSION* pSession)
|
||||||
|
{
|
||||||
|
return BinlogFilterSession::create(pSession, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void BinlogFilter::diagnostics(DCB* pDcb) const
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
json_t* BinlogFilter::diagnostics_json() const
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
uint64_t BinlogFilter::getCapabilities()
|
||||||
|
{
|
||||||
|
return RCAP_TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return BinlogFilter active state
|
||||||
|
bool BinlogFilter::is_active() const
|
||||||
|
{
|
||||||
|
return m_config.active;
|
||||||
|
}
|
||||||
93
server/modules/filter/binlogfilter/binlogfilter.hh
Normal file
93
server/modules/filter/binlogfilter/binlogfilter.hh
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <maxscale/cppdefs.hh>
|
||||||
|
#include <maxscale/filter.hh>
|
||||||
|
#include "binlogfiltersession.hh"
|
||||||
|
|
||||||
|
// Binlog Filter configuration
|
||||||
|
struct BinlogConfig
|
||||||
|
{
|
||||||
|
// Constructor
|
||||||
|
BinlogConfig(const MXS_CONFIG_PARAMETER* pParams)
|
||||||
|
: active(config_get_bool(pParams, "filter_events"))
|
||||||
|
, dbname(config_get_string(pParams, "skip_db"))
|
||||||
|
, table(config_get_string(pParams, "skip_table"))
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~BinlogConfig() {};
|
||||||
|
|
||||||
|
// Members mapped to config options
|
||||||
|
bool active;
|
||||||
|
std::string dbname;
|
||||||
|
std::string table;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BinlogFilter : public maxscale::Filter<BinlogFilter, BinlogFilterSession>
|
||||||
|
{
|
||||||
|
// Prevent copy-constructor and assignment operator usage
|
||||||
|
BinlogFilter(const BinlogFilter&);
|
||||||
|
BinlogFilter& operator = (const BinlogFilter&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
~BinlogFilter();
|
||||||
|
|
||||||
|
// Creates a new filter instance
|
||||||
|
static BinlogFilter* create(const char* zName,
|
||||||
|
char** pzOptions,
|
||||||
|
MXS_CONFIG_PARAMETER* ppParams);
|
||||||
|
|
||||||
|
// Creates a new session for this filter
|
||||||
|
BinlogFilterSession* newSession(MXS_SESSION* pSession);
|
||||||
|
|
||||||
|
// Print diagnostics to a DCB
|
||||||
|
void diagnostics(DCB* pDcb) const;
|
||||||
|
|
||||||
|
// Returns JSON form diagnostic data
|
||||||
|
json_t* diagnostics_json() const;
|
||||||
|
|
||||||
|
// Get filter capabilities
|
||||||
|
uint64_t getCapabilities();
|
||||||
|
|
||||||
|
// Filter is active
|
||||||
|
bool is_active() const;
|
||||||
|
|
||||||
|
// Return reference to filter config
|
||||||
|
const BinlogConfig& getConfig() const
|
||||||
|
{
|
||||||
|
return m_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Constructor: used in the create function
|
||||||
|
BinlogFilter(const MXS_CONFIG_PARAMETER* pParams);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Current configuration in maxscale.cnf
|
||||||
|
*
|
||||||
|
* [BinlogFilter]
|
||||||
|
* type=filter
|
||||||
|
* module=binlogfilter
|
||||||
|
* filter_events=On
|
||||||
|
* skip_table=t4
|
||||||
|
* skip_db=test
|
||||||
|
*
|
||||||
|
* Note: Only one table and one db right now
|
||||||
|
*/
|
||||||
|
BinlogConfig m_config;
|
||||||
|
};
|
||||||
261
server/modules/filter/binlogfilter/binlogfiltersession.cc
Normal file
261
server/modules/filter/binlogfilter/binlogfiltersession.cc
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// All log messages from this module are prefixed with this
|
||||||
|
#define MXS_MODULE_NAME "binlogfilter"
|
||||||
|
|
||||||
|
#include <maxscale/protocol/mysql.h>
|
||||||
|
#include "binlogfilter.hh"
|
||||||
|
#include "binlogfiltersession.hh"
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
// New packet which replaces the skipped events has 0 payload
|
||||||
|
#define NEW_PACKET_PAYLOD BINLOG_EVENT_HDR_LEN
|
||||||
|
|
||||||
|
// BinlogFilterSession constructor
|
||||||
|
BinlogFilterSession::BinlogFilterSession(MXS_SESSION* pSession,
|
||||||
|
const BinlogFilter* pFilter)
|
||||||
|
: mxs::FilterSession(pSession)
|
||||||
|
, m_filter(*pFilter)
|
||||||
|
, m_serverid(0)
|
||||||
|
, m_state(pFilter->is_active() ? COMMAND_MODE : INACTIVE)
|
||||||
|
, m_skip(false)
|
||||||
|
, m_complete_packet(true)
|
||||||
|
, m_crc(false)
|
||||||
|
{
|
||||||
|
MXS_NOTICE("Filter [%s] is %s",
|
||||||
|
MXS_MODULE_NAME,
|
||||||
|
m_filter.getConfig().active ? "enabled" : "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinlogFilterSession destructor
|
||||||
|
BinlogFilterSession::~BinlogFilterSession()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//static: create new session
|
||||||
|
BinlogFilterSession* BinlogFilterSession::create(MXS_SESSION* pSession,
|
||||||
|
const BinlogFilter* pFilter)
|
||||||
|
{
|
||||||
|
return new BinlogFilterSession(pSession, pFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// route input data from client
|
||||||
|
int BinlogFilterSession::routeQuery(GWBUF* pPacket)
|
||||||
|
{
|
||||||
|
if (m_state != INACTIVE)
|
||||||
|
{
|
||||||
|
uint8_t *data = GWBUF_DATA(pPacket);
|
||||||
|
|
||||||
|
// We assume OK indicator, the first byte after MYSQL_HEADER_LEN is 0
|
||||||
|
// TODO: check complete packet or
|
||||||
|
// at least MYSQL_HEADER_LEN + 1 + BINLOG_EVENT_HDR_LEN bytes
|
||||||
|
switch ((int)MYSQL_GET_COMMAND(data))
|
||||||
|
{
|
||||||
|
case COM_REGISTER_SLAVE:
|
||||||
|
// Connected client is registering as Slave Server
|
||||||
|
m_serverid = gw_mysql_get_byte4(data + MYSQL_HEADER_LEN + 1);
|
||||||
|
MXS_INFO("Client is registering as "
|
||||||
|
"Slave server with ID %" PRIu32 "",
|
||||||
|
m_serverid);
|
||||||
|
break;
|
||||||
|
case COM_BINLOG_DUMP:
|
||||||
|
// Connected Slave server waits for binlog events
|
||||||
|
m_state = BINLOG_MODE;
|
||||||
|
MXS_INFO("Slave server %" PRIu32 " is waiting for binlog events.",
|
||||||
|
m_serverid);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Connected client is using SQL mode
|
||||||
|
m_state = COMMAND_MODE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route input data
|
||||||
|
return mxs::FilterSession::routeQuery(pPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply data to client
|
||||||
|
int BinlogFilterSession::clientReply(GWBUF* pPacket)
|
||||||
|
{
|
||||||
|
if (m_state == BINLOG_MODE)
|
||||||
|
{
|
||||||
|
if (skipEvent(pPacket))
|
||||||
|
{
|
||||||
|
// Assuming ROW replication format:
|
||||||
|
// If transaction events needs to be skipped,
|
||||||
|
// they are replaced by an empty paylod packet
|
||||||
|
filterEvent(pPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send data
|
||||||
|
return mxs::FilterSession::clientReply(pPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// close session
|
||||||
|
void BinlogFilterSession::close()
|
||||||
|
{
|
||||||
|
if (m_state == BINLOG_MODE)
|
||||||
|
{
|
||||||
|
MXS_INFO("Slave server %" PRIu32 ": replication stopped.",
|
||||||
|
m_serverid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract binlog replication header from event data
|
||||||
|
static inline void extractHeader(register const uint8_t *event,
|
||||||
|
register REP_HEADER *hdr)
|
||||||
|
{
|
||||||
|
hdr->payload_len = gw_mysql_get_byte3(event);
|
||||||
|
hdr->seqno = event[3];
|
||||||
|
hdr->ok = event[MYSQL_HEADER_LEN];
|
||||||
|
hdr->timestamp = gw_mysql_get_byte4(event + MYSQL_HEADER_LEN + 1);
|
||||||
|
hdr->event_type = event[MYSQL_HEADER_LEN + 1 + 4];
|
||||||
|
// TODO: add offsets in order to facilitate reading
|
||||||
|
hdr->serverid = gw_mysql_get_byte4(event + MYSQL_HEADER_LEN + 1 + 4 + 1);
|
||||||
|
hdr->event_size = gw_mysql_get_byte4(event + MYSQL_HEADER_LEN + 1 + 4 + 1 + 4);
|
||||||
|
hdr->next_pos = gw_mysql_get_byte4(event + MYSQL_HEADER_LEN + 1 + 4 + 1 + 4 + 4);
|
||||||
|
hdr->flags = gw_mysql_get_byte2(event + MYSQL_HEADER_LEN + 1 + 4 + 1 + 4 + 4 + 4);
|
||||||
|
|
||||||
|
MXS_INFO("Slave server %" PRIu32 ": clientReply, event_type [%d], "
|
||||||
|
"flags %d, event_size %" PRIu32 ", next_pos %" PRIu32 ", packet size %" PRIu32 "",
|
||||||
|
hdr->serverid,
|
||||||
|
hdr->event_type,
|
||||||
|
hdr->flags,
|
||||||
|
hdr->event_size,
|
||||||
|
hdr->next_pos,
|
||||||
|
hdr->payload_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether events in a transaction can be skipped.
|
||||||
|
// The triggering event is TABLE_MAP_EVENT.
|
||||||
|
bool BinlogFilterSession::skipEvent(GWBUF* buffer)
|
||||||
|
{
|
||||||
|
uint8_t *event = GWBUF_DATA(buffer);
|
||||||
|
REP_HEADER hdr;
|
||||||
|
|
||||||
|
// Extract Replication header event from event data
|
||||||
|
extractHeader(event, &hdr);
|
||||||
|
|
||||||
|
if (hdr.ok == 0)
|
||||||
|
{
|
||||||
|
switch(hdr.event_type)
|
||||||
|
{
|
||||||
|
case TABLE_MAP_EVENT:
|
||||||
|
// Check db/table and set m_skip accordingly
|
||||||
|
skipDatabaseTable(event, hdr);
|
||||||
|
break;
|
||||||
|
case XID_EVENT:
|
||||||
|
// COMMIT: reset m_skip if set and set next pos to 0
|
||||||
|
if (m_skip)
|
||||||
|
{
|
||||||
|
m_skip = false;
|
||||||
|
// Some events skipped.
|
||||||
|
// Set next pos to 0 instead of real one.
|
||||||
|
gw_mysql_set_byte4(event + MYSQL_HEADER_LEN + 1 + 4 + 1 + 4 + 4, 0);
|
||||||
|
|
||||||
|
MXS_INFO("Skipped events: Setting next_pos = 0 in XID_EVENT");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Other events can be skipped or not, depending on m_skip value
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// m_skip could be true or false
|
||||||
|
return m_skip;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false; // always false: no filtering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract Dbname and Tabe name from TABLE_MAP event
|
||||||
|
static void inline extractTableInfo(const uint8_t *ptr,
|
||||||
|
char **dbname,
|
||||||
|
char **tblname)
|
||||||
|
{
|
||||||
|
// TODO: add offsets in order to facilitate reading
|
||||||
|
int db_len = *(ptr + MYSQL_HEADER_LEN + 1 + BINLOG_EVENT_HDR_LEN + 8);
|
||||||
|
|
||||||
|
*dbname = (char *)(ptr + MYSQL_HEADER_LEN + 1 + BINLOG_EVENT_HDR_LEN + 8 + 1);
|
||||||
|
*tblname = (char *)(ptr + MYSQL_HEADER_LEN + 1 + BINLOG_EVENT_HDR_LEN + 8 + 1 + db_len + 1 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether a db/table can be skipped based on configuration
|
||||||
|
void BinlogFilterSession::skipDatabaseTable(const uint8_t* data,
|
||||||
|
const REP_HEADER& hdr)
|
||||||
|
{
|
||||||
|
// Check for TABBLE_MAP event:
|
||||||
|
// Each time this event is seen the m_skip is overwritten
|
||||||
|
if (hdr.ok == 0 && hdr.event_type == TABLE_MAP_EVENT)
|
||||||
|
{
|
||||||
|
char *db = NULL;
|
||||||
|
char *table = NULL;
|
||||||
|
const BinlogConfig& fConfig = m_filter.getConfig();
|
||||||
|
|
||||||
|
// Get db/table
|
||||||
|
extractTableInfo(data, &db, &table);
|
||||||
|
|
||||||
|
// Check match with configuration
|
||||||
|
m_skip = (bool)(strcmp(db, fConfig.dbname.c_str()) == 0 ||
|
||||||
|
strcmp(table, fConfig.table.c_str()) == 0);
|
||||||
|
|
||||||
|
MXS_INFO("Dbname is [%s], Table is [%s], Skip [%s]\n",
|
||||||
|
db ? db : "",
|
||||||
|
table ? table : "",
|
||||||
|
m_skip ? "Yes" : "No");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the current event: no memory allocation
|
||||||
|
void BinlogFilterSession::filterEvent(GWBUF* pPacket)
|
||||||
|
{
|
||||||
|
ss_dassert(m_skip == true);
|
||||||
|
|
||||||
|
uint8_t *ptr = GWBUF_DATA(pPacket);
|
||||||
|
|
||||||
|
// Set NEW event_type
|
||||||
|
ptr[MYSQL_HEADER_LEN + 1 + 4] = RAND_EVENT;
|
||||||
|
// SET ignorable flags
|
||||||
|
gw_mysql_set_byte2(ptr + MYSQL_HEADER_LEN + 1 + 4 + 1 + 4 + 4 + 4,
|
||||||
|
LOG_EVENT_IGNORABLE_F | LOG_EVENT_SKIP_REPLICATION_F);
|
||||||
|
|
||||||
|
// Set event_len, size of empty rand_event (header + 0 bytes)
|
||||||
|
gw_mysql_set_byte4(ptr + MYSQL_HEADER_LEN + 1 + 4 + 1 + 4,
|
||||||
|
BINLOG_EVENT_HDR_LEN + 0);
|
||||||
|
|
||||||
|
// Set next pos to 0
|
||||||
|
gw_mysql_set_byte4(ptr + MYSQL_HEADER_LEN + 1 + 4 + 1 + 4 + 4, 0);
|
||||||
|
|
||||||
|
// Set New Packet size: even_len + 1 byte replication status
|
||||||
|
gw_mysql_set_byte3(ptr, BINLOG_EVENT_HDR_LEN + 0 + 1);
|
||||||
|
|
||||||
|
MXS_INFO("All events belonging to this table will be skipped");
|
||||||
|
|
||||||
|
MXS_INFO("Filtered event #%d,"
|
||||||
|
"ok %d, type %d, flags %d, size %d, next_pos %d, packet_size %d\n",
|
||||||
|
ptr[3],
|
||||||
|
ptr[4],
|
||||||
|
RAND_EVENT,
|
||||||
|
gw_mysql_get_byte2(ptr + MYSQL_HEADER_LEN + 1 + 4 + 1 + 4 + 4 + 4),
|
||||||
|
gw_mysql_get_byte4(ptr + MYSQL_HEADER_LEN + 1 + 4 + 1 + 4),
|
||||||
|
gw_mysql_get_byte4(ptr + MYSQL_HEADER_LEN + 1 + 4 + 1 + 4 + 4),
|
||||||
|
gw_mysql_get_byte3(ptr));
|
||||||
|
|
||||||
|
// Remove useless bytes
|
||||||
|
pPacket = gwbuf_rtrim(pPacket,
|
||||||
|
gwbuf_length(pPacket) - (BINLOG_EVENT_HDR_LEN + 1 + 4));
|
||||||
|
}
|
||||||
98
server/modules/filter/binlogfilter/binlogfiltersession.hh
Normal file
98
server/modules/filter/binlogfilter/binlogfiltersession.hh
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <maxscale/cppdefs.hh>
|
||||||
|
#include <maxscale/filter.hh>
|
||||||
|
#include "binlogfilter.hh"
|
||||||
|
|
||||||
|
//TODO: add in a separate .h file in common to BinlogRouter code
|
||||||
|
#define LOG_EVENT_IGNORABLE_F 0x0080
|
||||||
|
#define LOG_EVENT_SKIP_REPLICATION_F 0x8000
|
||||||
|
#define RAND_EVENT 0x000D
|
||||||
|
#define TABLE_MAP_EVENT 0x0013
|
||||||
|
#define XID_EVENT 0x0010
|
||||||
|
#define BINLOG_EVENT_HDR_LEN 19
|
||||||
|
|
||||||
|
typedef struct rep_header_t
|
||||||
|
{
|
||||||
|
int payload_len; /*< Payload length (24 bits) */
|
||||||
|
uint8_t seqno; /*< Response sequence number */
|
||||||
|
uint8_t ok; /*< OK Byte from packet */
|
||||||
|
uint32_t timestamp; /*< Timestamp - start of binlog record */
|
||||||
|
uint8_t event_type; /*< Binlog event type */
|
||||||
|
uint32_t serverid; /*< Server id of master */
|
||||||
|
uint32_t event_size; /*< Size of header, post-header and body */
|
||||||
|
uint32_t next_pos; /*< Position of next event */
|
||||||
|
uint16_t flags; /*< Event flags */
|
||||||
|
} REP_HEADER;
|
||||||
|
// End TODO
|
||||||
|
//
|
||||||
|
class BinlogFilter;
|
||||||
|
|
||||||
|
class BinlogFilterSession : public maxscale::FilterSession
|
||||||
|
{
|
||||||
|
// Prevent copy-constructor and assignment operator usage
|
||||||
|
BinlogFilterSession(const BinlogFilterSession&);
|
||||||
|
BinlogFilterSession& operator = (const BinlogFilterSession&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
~BinlogFilterSession();
|
||||||
|
|
||||||
|
// Create a new filter session
|
||||||
|
static BinlogFilterSession* create(MXS_SESSION* pSession,
|
||||||
|
const BinlogFilter* pFilter);
|
||||||
|
|
||||||
|
// Called when a client session has been closed
|
||||||
|
void close();
|
||||||
|
|
||||||
|
// Handle a query from the client
|
||||||
|
int routeQuery(GWBUF* pPacket);
|
||||||
|
|
||||||
|
// Handle a reply from server
|
||||||
|
int clientReply(GWBUF* pPacket);
|
||||||
|
|
||||||
|
// Whether to skip current event
|
||||||
|
bool skipEvent(GWBUF* data);
|
||||||
|
|
||||||
|
// Skip database/table events in current trasaction
|
||||||
|
void skipDatabaseTable(const uint8_t* data, const REP_HEADER& hdr);
|
||||||
|
|
||||||
|
// Filter the replication event
|
||||||
|
void filterEvent(GWBUF* data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Used in the create function
|
||||||
|
BinlogFilterSession(MXS_SESSION* pSession, const BinlogFilter* pFilter);
|
||||||
|
|
||||||
|
// Reference to Filter instance
|
||||||
|
const BinlogFilter& m_filter;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Internal states for filter operations
|
||||||
|
enum state_t
|
||||||
|
{
|
||||||
|
INACTIVE, // Fitering is not active
|
||||||
|
COMMAND_MODE, // Connected client in SQL mode: no filtering
|
||||||
|
BINLOG_MODE // Connected client in BINLOG_MODE: filter events
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Event filtering member vars
|
||||||
|
uint32_t m_serverid; // server-id of connected slave
|
||||||
|
state_t m_state; // Internal state
|
||||||
|
bool m_skip; // Mark event skipping
|
||||||
|
bool m_complete_packet; // A complete received. Not implemented
|
||||||
|
bool m_crc; // CRC32 for events. Not implemented
|
||||||
|
bool m_large_payload; // Packet bigger than 16MB. Not implemented
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user