YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense alongwiththisprogram;ifnot,writetotheFreeSoftware
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
enum fsp_binlog_chunk_types { /* Zero means no data, effectively EOF. */
FSP_BINLOG_TYPE_EMPTY= 0, /* A binlogged committed event group. */
FSP_BINLOG_TYPE_COMMIT= 1, /* A binlog GTID state record. */
FSP_BINLOG_TYPE_GTID_STATE= 2, /* Out-of-band event group data. */
FSP_BINLOG_TYPE_OOB_DATA= 3, /* Dummy record, use to fill remainder of page (eg. FLUSH BINARY LOGS). */
FSP_BINLOG_TYPE_DUMMY= 4, /* Must be one more than the last type. */
FSP_BINLOG_TYPE_END,
/* Padding data at end of page. */
FSP_BINLOG_TYPE_FILLER= 0xff
}; static constexpr uint32_t FSP_BINLOG_FLAG_BIT_CONT= 7; static constexpr uint32_t FSP_BINLOG_FLAG_CONT= (1 << FSP_BINLOG_FLAG_BIT_CONT); static constexpr uint32_t FSP_BINLOG_FLAG_BIT_LAST= 6; static constexpr uint32_t FSP_BINLOG_FLAG_LAST= (1 << FSP_BINLOG_FLAG_BIT_LAST); static constexpr uint32_t FSP_BINLOG_TYPE_MASK=
~(FSP_BINLOG_FLAG_CONT | FSP_BINLOG_FLAG_LAST); static constexpr uint64_t ALLOWED_NESTED_RECORDS= /* GTID STATE at start of page can occur in the middle of other record. */
((uint64_t)1 << FSP_BINLOG_TYPE_GTID_STATE) | /* DUMMY data at tablespace end can occur in the middle of other record. */
((uint64_t)1 << FSP_BINLOG_TYPE_DUMMY)
;
/* Currentstate,canbeobtainedfromsave_pos()andlaterpassedto restore_pos().
*/ struct saved_position { /* Current position file. */
uint64_t file_no; /* Current position page. */
uint32_t page_no; /* Start of current chunk inside page. */
uint32_t in_page_offset; /* Thelengthofthecurrentchunk,oncethechunktypehasbeenread. If0,itmeansthechunktype(andlength)hasnotyetbeenread.
*/
uint32_t chunk_len; /* The read position inside the current chunk. */
uint32_t chunk_read_offset;
uchar chunk_type; /* When set, read will skip the current chunk, if any. */ bool skip_current; /* Set while we are in the middle of reading a record. */ bool in_record;
} s;
/* Length of the currently open file, valid if cur_file_handle != -1. */
uint64_t cur_file_length; /* Buffer for reading a page from a binlog file. */
uchar *page_buffer; /* Open file handle to tablespace file_no, or -1. */
File cur_file_handle; /* Flagusedtoskiptherestofanypartialchunkwemightbestartingin themiddleof.
*/ bool skipping_partial; /* If the s.file_no / s.page_no is loaded in the page buffer. */ bool page_loaded;
Returnsnumberofbytesread,or-1forerror. Returns0ifthechunk_readerispointingtostartofachunkattheend ofthecurrentbinlog(ie.end-of-file).
*/ int read_data(uchar *buffer, int max_len, bool multipage); /* Read the file header of current file_no. */ int parse_file_header();
/* Save current position, and restore it later. */ void save_pos(saved_position *out_pos) { *out_pos= s; } void restore_pos(saved_position *pos); void seek(uint64_t file_no, uint64_t offset);
/* Makenextread_data()skipanydatafromthecurrentchunk(ifany),and
start reading data only from the beginning of the next chunk. */ void skip_current() { if (s.in_record) s.skip_current= true; } /* Usedinitially,afterseekingpotentiallyintothemiddleofa(commit) record,toskipanycontinuationchunksuntilwereachthestartofthe firstrealrecord.
*/ void skip_partial(bool skip) { skipping_partial= skip; }
uint64_t current_pos() { return (s.page_no * binlog_page_size) + s.in_page_offset;
} void set_fd(File fd);
};
class oob_reader_mysqlbinlog { enum oob_states { /* The initial state, about to visit the node for the first time. */
ST_initial, /* State of leaf node while traversing the prior trees in the forest. */
ST_traversing_prior_trees, /* State of non-leaf node while traversing its left sub-tree. */
ST_traversing_left_child, /* State of non-leaf node while traversing its right sub-tree. */
ST_traversing_right_child, /* State of node while reading out its data. */
ST_self
};
/* Stackentryforonenodecurrentlytakingpartinpost-ordertraversal. Wemaintainastackofpendingnodesduringthetraversal,asthetraversal happensinastatemachineratherthanbyrecursion.
*/ struct stack_entry { /* Saved position after reading header. */
chunk_reader_mysqlbinlog::saved_position saved_pos; /* The location of this node's OOB record. */
uint64_t file_no;
uint64_t offset; /* Right child, to be traversed after left child. */
uint64_t right_file_no;
uint64_t right_offset; /* Offset of real data in this node, after header. */
uint32_t header_len; /* Amount of data read into rd_buf, and amount used to parse header. */
uint32_t rd_buf_len;
uint32_t rd_buf_sofar; /* Current state in post-order traversal state machine. */ enum oob_states state; /* Buffer for reading header. */
uchar rd_buf[5*COMPR_INT_MAX64]; /* Truewhenthenodeisreachedusingonlyleftchildpointers,false otherwise.Usedtoidentifytheleft-mostleafinatreewhichpointsto apriortreethatmustbetraversedfirst.
*/ bool is_leftmost;
};
std::vector<stack_entry>stack;
/* State machine current state. */ enum oob_states state;
/* Out-of-band data to read after commit record, if any. */
uint64_t oob_count;
uint64_t oob_last_file_no;
uint64_t oob_last_offset; /* Any secondary out-of-band data to be also read. */
uint64_t oob_count2;
uint64_t oob_last_file_no2;
uint64_t oob_last_offset2; /* Thestartingfile_no.Westoponcewe'vereadthelastrecordinthisfile (whichmayspanintothenextfile).
*/
uint64_t start_file_no; /* Buffer to hold a page read directly from the binlog file. */
uchar *page_buf; /* Keep track of pending bytes in the rd_buf. */
uint32_t rd_buf_len;
uint32_t rd_buf_sofar; /* State for state machine reading chunks one by one. */ enum reader_states state;
/* Used to read the header of the commit record. */
uchar rd_buf[5*COMPR_INT_MAX64]; private: int read_data(uchar *buf, uint32_t len);
if(s.in_page_offset<BINLOG_PAGE_DATA) s.in_page_offset=BINLOG_PAGE_DATA;
*/ if (0)
static_assert(BINLOG_PAGE_DATA == 0, "Replace static_assert with code from above comment");
/* Check for end-of-file. */ if ((s.page_no * binlog_page_size) + s.in_page_offset >= cur_file_length) return sofar;
/* Now we have a chunk available to read data from. */
DBUG_ASSERT(s.in_page_offset + s.chunk_len + 3 <=
binlog_page_size - BINLOG_PAGE_DATA_END);
DBUG_ASSERT(s.chunk_read_offset < s.chunk_len); if (s.skip_current &&
(s.chunk_read_offset > 0 || (s.chunk_type & FSP_BINLOG_FLAG_CONT)))
{ /* Skipinitialcontinuationchunks. Usedtobeabletostartreadingpotentiallyinthemiddleofarecord, ie.ataGTIDstatepoint.
*/
s.chunk_read_offset= s.chunk_len;
} else
{
size= std::min((uint32_t)max_len, s.chunk_len - s.chunk_read_offset);
memcpy(buffer, page_buffer + s.in_page_offset + 3 + s.chunk_read_offset, size);
buffer+= size;
s.chunk_read_offset+= size;
max_len-= size;
sofar+= size;
}
if (s.chunk_len > s.chunk_read_offset)
{
DBUG_ASSERT(max_len == 0/* otherwise would have read more */); return sofar;
}
/* We have read all of the chunk. Move to next chunk or end of the record. */
skip_chunk:
DBUG_ASSERT(s.in_page_offset + s.chunk_len + 3 <=
binlog_page_size - BINLOG_PAGE_DATA_END);
s.in_page_offset+= 3 + s.chunk_len;
s.chunk_len= 0;
s.chunk_read_offset= 0;
if (s.chunk_type & FSP_BINLOG_FLAG_LAST)
{
s.in_record= false; /* End of record. */
s.skip_current= false;
}
if (s.in_page_offset >= binlog_page_size - (BINLOG_PAGE_DATA_END + 3) &&
(s.page_no * binlog_page_size) + s.in_page_offset < cur_file_length)
{
go_next_page: /* End of page reached, move to the next page. */
++s.page_no;
page_loaded= false;
s.in_page_offset= 0;
if (cur_file_handle >= (File)0 &&
(s.page_no * binlog_page_size) >= cur_file_length)
{ /* Move to the next file. */
my_close(cur_file_handle, MYF(0));
cur_file_handle= (File)-1;
cur_file_length= ~(uint64_t)0;
++s.file_no;
s.page_no= 1; /* Skip the header page. */
}
}
if (sofar > 0 && (!multipage || !s.in_record)) return sofar;
case ST_traversing_prior_trees:
chunk_rd->restore_pos(&e->saved_pos);
e->state= ST_self; goto again;
case ST_traversing_left_child:
e->state= ST_traversing_right_child;
push_state(ST_initial, e->right_file_no, e->right_offset, false); goto again;
case ST_traversing_right_child:
chunk_rd->restore_pos(&e->saved_pos);
e->state= ST_self; goto again;
case ST_self:
size= 0; if (e->rd_buf_len > e->rd_buf_sofar)
{ /* Use any excess data from when the header was read. */
size= std::min((int)(e->rd_buf_len - e->rd_buf_sofar), len);
memcpy(buf, e->rd_buf + e->rd_buf_sofar, size);
e->rd_buf_sofar+= size;
len-= size;
buf+= size;
}
if (len > 0 && !chunk_rd->end_of_record())
{
res= chunk_rd->read_data(buf, len, false); if (res < 0) return -1;
size+= res;
}
if (chunk_rd->end_of_record())
{ /* This oob record done, pop the state. */
DBUG_ASSERT(!stack.empty());
stack.erase(stack.end() - 1, stack.end());
} return size;
void
chunk_reader_mysqlbinlog::set_fd(File fd)
{ if (cur_file_handle != (File)-1)
{
my_close(cur_file_handle, MYF(0));
cur_file_length= ~(uint64_t)0;
page_loaded= false;
}
cur_file_handle= fd;
my_off_t old_pos= my_tell(fd, MYF(0)); if (old_pos != (my_off_t)-1)
{ /* Will be ~0 if we cannot seek the file. */
cur_file_length= my_seek(fd, 0, SEEK_END, MYF(0));
my_seek(fd, old_pos, SEEK_SET, MYF(0));
}
}
bool
binlog_reader_innodb::data_available()
{
DBUG_ASSERT(0/* Should not be used in mysqlbinlog. */); returntrue;
}
bool
binlog_reader_innodb::wait_available(THD *thd, conststruct timespec *abstime)
{
DBUG_ASSERT(0/* Should not be used in mysqlbinlog. */); returntrue;
}
int
binlog_reader_innodb::init_gtid_pos(THD *thd, slave_connection_state *pos,
rpl_binlog_state_base *state)
{
DBUG_ASSERT(0/* Should not be used in mysqlbinlog. */); return1;
}
int
binlog_reader_innodb::init_legacy_pos(THD *thd, constchar *filename,
ulonglong offset)
{
DBUG_ASSERT(0/* Should not be used in mysqlbinlog. */); return1;
}
void
binlog_reader_innodb::enable_single_file()
{
DBUG_ASSERT(0/* Should not be used in mysqlbinlog. */);
}
int
binlog_reader_innodb::read_binlog_data(uchar *buf, uint32_t len)
{ int res= read_data(buf, len); return res;
}
bool
binlog_reader_innodb::init_from_fd_pos(File fd, ulonglong start_position)
{
chunk_rd.set_fd(fd); if (chunk_rd.parse_file_header()) returntrue;
uint64_t prev_start_file_no= start_file_no;
start_file_no= chunk_rd.s.file_no; if (prev_start_file_no != ~(uint64_t)0 &&
prev_start_file_no + 1 == chunk_rd.s.file_no)
{ /* Continuing in the file following the previous one. */
} else
{ if (start_position < binlog_page_size)
start_position= binlog_page_size;
chunk_rd.seek(chunk_rd.s.file_no, (uint64_t)start_position);
chunk_rd.skip_partial(true);
} returnfalse;
}
int binlog_reader_innodb::read_data(uchar *buf, uint32_t len)
{ int res; const uchar *p_end; const uchar *p;
std::pair<uint64_t, constunsignedchar *> v_and_p; int sofar= 0;
again: switch (state)
{ case ST_read_next_event_group: if (chunk_rd.s.file_no > start_file_no ||
(chunk_rd.s.file_no == start_file_no && chunk_rd.is_end_of_file()))
{ /* Wehavereadtheentirefile,returnEOF. Iftheuserspecifiedtoreadthefollowingfilealso,wemay continuewhereweleftinthatfilelater.
*/ return sofar;
}
static_assert(sizeof(rd_buf) == 5*COMPR_INT_MAX64, "rd_buf size must match code using it");
res= chunk_rd.read_data(rd_buf, 5*COMPR_INT_MAX64, true); if (res < 0) return res; if (res == 0) return sofar; if (chunk_rd.cur_type() != FSP_BINLOG_TYPE_COMMIT)
{
chunk_rd.skip_current(); goto again;
} /* Found the start of a commit record. */
chunk_rd.skip_partial(false);
/* Read the header of the commit record to see if there's any oob data. */
rd_buf_len= res;
p_end= rd_buf + res;
v_and_p= compr_int_read(rd_buf);
p= v_and_p.second; if (p > p_end) return chunk_rd.read_error_corruption("Short chunk");
oob_count= v_and_p.first;
oob_count2= 0;
if (oob_count > 0)
{ /* Skip the pointer to first chunk. */
v_and_p= compr_int_read(p);
p= v_and_p.second; if (p > p_end) return chunk_rd.read_error_corruption("Short chunk");
v_and_p= compr_int_read(p);
p= v_and_p.second; if (p > p_end) return chunk_rd.read_error_corruption("Short chunk");
case ST_read_commit_record: if (rd_buf_len > rd_buf_sofar)
{ /* Use any excess data from when the header was read. */ int size= std::min((int)(rd_buf_len - rd_buf_sofar), (int)len);
memcpy(buf, rd_buf + rd_buf_sofar, size);
rd_buf_sofar+= size;
len-= size;
buf+= size;
sofar+= size;
}
offset= (s.page_no * binlog_page_size) | s.in_page_offset; if (offset >= cur_file_length) { /* End of this file, move to the next one. */
goto_next_file: if (cur_file_handle >= (File)0)
{
my_close(cur_file_handle, MYF(0));
cur_file_handle= (File)-1;
cur_file_length= ~(uint64_t)0;
}
++s.file_no;
s.page_no= 1; /* Skip the header page. */ continue;
} break;
}
int res= read_page_mysqlbinlog(cur_file_handle, page_buffer, s.page_no); if (res < 0) return CHUNK_READER_ERROR; if (res == 0) goto goto_next_file;
page_loaded= true; return CHUNK_READER_FOUND;
}
void
chunk_reader_mysqlbinlog::restore_pos(chunk_reader_mysqlbinlog::saved_position *pos)
{ if (cur_file_handle != (File)-1 && pos->file_no != s.file_no)
{ /* Seek to a different file than currently open, close it. */
my_close(cur_file_handle, MYF(0));
cur_file_handle= (File)-1;
cur_file_length= ~(uint64_t)0;
}
s= *pos;
page_loaded= false;
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.31Bemerkung:
(vorverarbeitet am 2026-10-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.