1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00
Files
mariadb/sql/rpl_tblmap.h
Brandon Nesterenko 5fda8988a6 MDEV-32570 (client): Fragment ROW replication events larger than slave_max_allowed_packet
This patch extends mysqlbinlog with logic to support the output and
replay of the new Partial_rows_log_events added in the previous
commit. Generally speaking, as the assembly and execution of the
Rows_log_event happens in Partial_rows_log_event::do_apply_event();
there isn’t much logic required other than outputting
Partial_rows_log_event in base64. With two exceptions..

In the original mysqlbinlog code, all row events fit within a single
BINLOG base64 statement; such that the Table_map_log_event sets up
the tables to use, the Row Events open the tables, and then after
the BINLOG statement is run, the tables are closed and the rgi is
destroyed. No matter how many Row Events within a transaction there
are, they are all put into the same BINLOG base64 statement.
However, for the new Partial_rows_log_event, each fragment is split
into its own BINLOG base64 statement (to respect the server’s
configured max_packet_size). The existing logic would close the
tables and destroy the replay context after each BINLOG statement
(i.e. each fragment). This means that 1) Partial_rows_log_events
would be un-able to assemble Rows_log_events because the rgi is
destroyed between events, and 2) multiple re-assembled
Rows_log_events could not be executed because the context set-up by
the Table_map_log_event is cleared after the first Rows_log_event
executes.

To fix the first problem, where we couldn’t re-assemble
Rows_log_events because the rgi would disappear between
Partial_rows_log_events, the server will not destroy the rgi when
ingesting BINLOG statements containing Partial_rows_log_events that
have not yet assembled their Rows_log_event.

To fix the second problem, where the context set-up by the
Table_map_log_event is cleared after the first assembled
Rows_log_event executes, mysqlbinlog caches the Table_map_log_event
to re-write for each fragmented Rows_log_event at the start of the
last fragment’s BINLOG statement. In effect, this will re-execute
the Table_map_log_event for each assembled Rows_log_event.

Reviewed-by: Hemant Dangi <hemant.dangi@mariadb.com>
Acked-by: Kristian Nielsen <knielsen@knielsen-hq.org>
Signed-off-by: Brandon Nesterenko <brandon.nesterenko@mariadb.com>
2026-01-27 12:59:23 -07:00

114 lines
3.2 KiB
C++

/* Copyright (c) 2005, 2010, 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-1335 USA */
#ifndef TABLE_MAPPING_H
#define TABLE_MAPPING_H
/* Forward declarations */
#ifndef MYSQL_CLIENT
struct TABLE;
#else
class Table_map_log_event;
typedef Table_map_log_event TABLE;
void free_table_map_log_event(TABLE *table);
#endif
/*
CLASS table_mapping
RESPONSIBILITIES
The table mapping is used to map table id's to table pointers
COLLABORATION
RELAY_LOG For mapping table id:s to tables when receiving events.
*/
/*
Guilhem to Mats:
in the table_mapping class, the memory is allocated and never freed (until
destruction). So this is a good candidate for allocating inside a MEM_ROOT:
it gives the efficient allocation in chunks (like in expand()). So I have
introduced a MEM_ROOT.
Note that inheriting from Sql_alloc had no effect: it has effects only when
"ptr= new table_mapping" is called, and this is never called. And it would
then allocate from thd->mem_root which is a highly volatile object (reset
from example after executing each query, see dispatch_command(), it has a
free_root() at end); as the table_mapping object is supposed to live longer
than a query, it was dangerous.
A dedicated MEM_ROOT needs to be used, see below.
*/
#include "hash.h" /* HASH */
class table_mapping {
private:
MEM_ROOT m_mem_root;
public:
enum enum_error {
ERR_NO_ERROR = 0,
ERR_LIMIT_EXCEEDED,
ERR_MEMORY_ALLOCATION
};
table_mapping();
~table_mapping();
TABLE* get_table(ulonglong table_id);
int set_table(ulonglong table_id, TABLE* table);
int remove_table(ulonglong table_id);
void clear_tables();
ulong count() const { return m_table_ids.records; }
void get_table_ids(ulonglong *table_ids, size_t n_table_ids);
private:
/*
This is a POD (Plain Old Data). Keep it that way (we apply offsetof() to
it, which only works for PODs)
*/
struct entry {
ulonglong table_id;
union {
TABLE *table;
entry *next;
};
};
entry *find_entry(ulonglong table_id)
{
return (entry *) my_hash_search(&m_table_ids,
(uchar*)&table_id,
sizeof(table_id));
}
int expand();
/*
Head of the list of free entries; "free" in the sense that it's an
allocated entry free for use, NOT in the sense that it's freed
memory.
*/
entry *m_free;
/* Correspondence between an id (a number) and a TABLE object */
HASH m_table_ids;
};
#endif