294 lines
7.8 KiB
C++
294 lines
7.8 KiB
C++
/*
|
|
* Copyright (c) 2018 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: 2023-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.
|
|
*/
|
|
#pragma once
|
|
|
|
#include <vector>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <memory>
|
|
#include <unordered_map>
|
|
|
|
#include <maxscale/pcre2.h>
|
|
#include <maxscale/service.hh>
|
|
#include <binlog_common.hh>
|
|
|
|
typedef std::vector<uint8_t> Bytes;
|
|
|
|
// A GTID position
|
|
struct gtid_pos_t
|
|
{
|
|
gtid_pos_t()
|
|
: timestamp(0)
|
|
, domain(0)
|
|
, server_id(0)
|
|
, seq(0)
|
|
, event_num(0)
|
|
{
|
|
}
|
|
|
|
uint32_t timestamp; /*< GTID event timestamp */
|
|
uint64_t domain; /*< Replication domain */
|
|
uint64_t server_id; /*< Server ID */
|
|
uint64_t seq; /*< Sequence number */
|
|
uint64_t event_num; /*< Subsequence number, increases monotonically. This
|
|
* is an internal representation of the position of
|
|
* an event inside a GTID event and it is used to
|
|
* rebuild GTID events in the correct order. */
|
|
|
|
void extract(const REP_HEADER& hdr, uint8_t* ptr);
|
|
bool parse(const char* str);
|
|
static gtid_pos_t from_string(std::string str);
|
|
std::string to_string() const;
|
|
bool empty() const;
|
|
};
|
|
|
|
/** A single column in a CREATE TABLE statement */
|
|
struct Column
|
|
{
|
|
Column(std::string name, std::string type = "unknown", int length = -1)
|
|
: name(name)
|
|
, type(type)
|
|
, length(length)
|
|
{
|
|
}
|
|
|
|
std::string name;
|
|
std::string type;
|
|
int length;
|
|
|
|
json_t* to_json() const;
|
|
static Column from_json(json_t* json);
|
|
};
|
|
|
|
struct TableCreateEvent;
|
|
typedef std::shared_ptr<TableCreateEvent> STableCreateEvent;
|
|
|
|
/** A CREATE TABLE abstraction */
|
|
struct TableCreateEvent
|
|
{
|
|
TableCreateEvent(std::string db, std::string table, int version, std::vector<Column>&& cols)
|
|
: columns(cols)
|
|
, table(table)
|
|
, database(db)
|
|
, version(version)
|
|
, was_used(false)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Get the table identifier i.e. `database.table`
|
|
*
|
|
* @return The table identifier
|
|
*/
|
|
std::string id() const
|
|
{
|
|
return database + '.' + table;
|
|
}
|
|
|
|
/**
|
|
* Convert to JSON
|
|
*
|
|
* @return JSON representation of this object
|
|
*/
|
|
json_t* to_json() const;
|
|
|
|
/**
|
|
* Convert from JSON
|
|
*
|
|
* @param json JSON to convert from
|
|
*
|
|
* @return Object representation of JSON if it is valid or empty pointer if invalid.
|
|
*/
|
|
static STableCreateEvent from_json(json_t* json);
|
|
|
|
std::vector<Column> columns;
|
|
std::string table;
|
|
std::string database;
|
|
int version; /**< How many versions of this table have been used */
|
|
bool was_used; /**< Has this schema been persisted to disk */
|
|
};
|
|
|
|
/** A representation of a table map event read from a binary log. A table map
|
|
* maps a table to a unique ID which can be used to match row events to table map
|
|
* events. The table map event tells us how the table is laid out and gives us
|
|
* some meta information on the columns. */
|
|
struct TableMapEvent
|
|
{
|
|
TableMapEvent(const std::string& db,
|
|
const std::string& table,
|
|
uint64_t id,
|
|
int version,
|
|
Bytes&& cols,
|
|
Bytes&& nulls,
|
|
Bytes&& metadata)
|
|
: database(db)
|
|
, table(table)
|
|
, id(id)
|
|
, version(version)
|
|
, column_types(cols)
|
|
, null_bitmap(nulls)
|
|
, column_metadata(metadata)
|
|
{
|
|
}
|
|
|
|
uint64_t columns() const
|
|
{
|
|
return column_types.size();
|
|
}
|
|
|
|
std::string database;
|
|
std::string table;
|
|
uint64_t id;
|
|
int version;
|
|
Bytes column_types;
|
|
Bytes null_bitmap;
|
|
Bytes column_metadata;
|
|
};
|
|
|
|
typedef std::shared_ptr<TableMapEvent> STableMapEvent;
|
|
|
|
// Containers for the replication events
|
|
typedef std::unordered_map<std::string, STableCreateEvent> CreatedTables;
|
|
typedef std::unordered_map<std::string, STableMapEvent> MappedTables;
|
|
typedef std::unordered_map<uint64_t, STableMapEvent> ActiveMaps;
|
|
|
|
// Handler class for row based replication events
|
|
class RowEventHandler
|
|
{
|
|
public:
|
|
virtual ~RowEventHandler()
|
|
{
|
|
}
|
|
|
|
// A table was created
|
|
virtual bool create_table(const STableCreateEvent& create)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// A table was opened
|
|
virtual bool open_table(const STableMapEvent& map, const STableCreateEvent& create)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Prepare a table for row processing
|
|
virtual bool prepare_table(const STableMapEvent& map, const STableCreateEvent& create)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Flush open tables
|
|
virtual void flush_tables()
|
|
{
|
|
}
|
|
|
|
// Prepare a new row for processing
|
|
virtual void prepare_row(const gtid_pos_t& gtid, const REP_HEADER& hdr, int event_type) = 0;
|
|
|
|
// Called once all columns are processed
|
|
virtual bool commit(const gtid_pos_t& gtid) = 0;
|
|
|
|
// 32-bit integer handler
|
|
virtual void column(int i, int32_t value) = 0;
|
|
|
|
// 64-bit integer handler
|
|
virtual void column(int i, int64_t value) = 0;
|
|
|
|
// Float handler
|
|
virtual void column(int i, float value) = 0;
|
|
|
|
// Double handler
|
|
virtual void column(int i, double value) = 0;
|
|
|
|
// String handler
|
|
virtual void column(int i, std::string value) = 0;
|
|
|
|
// Bytes handler
|
|
virtual void column(int i, uint8_t* value, int len) = 0;
|
|
|
|
// Empty (NULL) value type handler
|
|
virtual void column(int i) = 0;
|
|
};
|
|
|
|
typedef std::auto_ptr<RowEventHandler> SRowEventHandler;
|
|
|
|
class Rpl
|
|
{
|
|
public:
|
|
Rpl(const Rpl&) = delete;
|
|
Rpl& operator=(const Rpl&) = delete;
|
|
|
|
// Construct a new replication stream transformer
|
|
Rpl(SERVICE* service,
|
|
SRowEventHandler event_handler,
|
|
pcre2_code* match,
|
|
pcre2_code* exclude,
|
|
gtid_pos_t =
|
|
{
|
|
});
|
|
|
|
// Add a stored TableCreateEvent
|
|
void add_create(STableCreateEvent create);
|
|
|
|
// Handle a replicated binary log event
|
|
void handle_event(REP_HEADER hdr, uint8_t* ptr);
|
|
|
|
// Called when processed events need to be persisted to disk
|
|
void flush();
|
|
|
|
// Check if binlog checksums are enabled
|
|
bool have_checksums() const
|
|
{
|
|
return m_binlog_checksum;
|
|
}
|
|
|
|
// Set current GTID
|
|
void set_gtid(gtid_pos_t gtid)
|
|
{
|
|
m_gtid = gtid;
|
|
}
|
|
|
|
// Get current GTID
|
|
const gtid_pos_t& get_gtid() const
|
|
{
|
|
return m_gtid;
|
|
}
|
|
|
|
private:
|
|
SRowEventHandler m_handler;
|
|
SERVICE* m_service;
|
|
pcre2_code* m_create_table_re;
|
|
pcre2_code* m_alter_table_re;
|
|
uint8_t m_binlog_checksum;
|
|
uint8_t m_event_types;
|
|
Bytes m_event_type_hdr_lens;
|
|
gtid_pos_t m_gtid;
|
|
ActiveMaps m_active_maps;
|
|
MappedTables m_table_maps;
|
|
CreatedTables m_created_tables;
|
|
pcre2_code* m_match;
|
|
pcre2_code* m_exclude;
|
|
pcre2_match_data* m_md_match;
|
|
pcre2_match_data* m_md_exclude;
|
|
|
|
void handle_query_event(REP_HEADER* hdr, uint8_t* ptr);
|
|
bool handle_table_map_event(REP_HEADER* hdr, uint8_t* ptr);
|
|
bool handle_row_event(REP_HEADER* hdr, uint8_t* ptr);
|
|
STableCreateEvent table_create_copy(const char* sql, size_t len, const char* db);
|
|
bool save_and_replace_table_create(STableCreateEvent created);
|
|
bool table_create_alter(STableCreateEvent create, const char* sql, const char* end);
|
|
bool table_matches(const std::string& ident);
|
|
};
|