298 lines
11 KiB
C++
298 lines
11 KiB
C++
/* Copyright (c) 2007, 2024, Oracle and/or its affiliates.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is designed to work with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have either included with
|
|
the program or referenced in the documentation.
|
|
|
|
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, version 2.0, 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-1301 USA */
|
|
|
|
#ifndef RPL_RECORD_H
|
|
#define RPL_RECORD_H
|
|
|
|
#include <stddef.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "my_inttypes.h"
|
|
|
|
class Relay_log_info;
|
|
struct TABLE;
|
|
|
|
struct MY_BITMAP;
|
|
|
|
class table_def;
|
|
class Bit_reader;
|
|
|
|
enum class enum_row_image_type { WRITE_AI, UPDATE_BI, UPDATE_AI, DELETE_BI };
|
|
|
|
#if defined(MYSQL_SERVER)
|
|
size_t pack_row(TABLE *table, MY_BITMAP const *cols, uchar *row_data,
|
|
const uchar *data, enum_row_image_type row_image_type,
|
|
ulonglong value_options = 0);
|
|
|
|
/**
|
|
Unpack a row image (either before-image or after-image) into @c
|
|
table->record[0].
|
|
|
|
The row is assumed to only consist of the fields for which the
|
|
corresponding bit in bitset @c column_image is set; the other parts
|
|
of the record are left alone.
|
|
|
|
If the replica table has more columns than the source table, then the
|
|
extra columns are not touched by this function. If the source table
|
|
has more columns than the replica table, then the position is moved to
|
|
after the extra columns, but the values are not used.
|
|
|
|
If the replica has a GIPK and the source does not, then the extra column
|
|
is not touched by this function. If the source table has a GIPK and the
|
|
replica does not, then the position is shifted forward by 1.
|
|
|
|
- The layout of a row is:
|
|
|
|
For WRITE_ROWS_EVENT:
|
|
+--------------+
|
|
| after-image |
|
|
+--------------+
|
|
|
|
For DELETE_ROWS_EVENT:
|
|
+--------------+
|
|
| before-image |
|
|
+--------------+
|
|
|
|
For UPDATE_ROWS_EVENT:
|
|
+--------------+-------------+
|
|
| before-image | after-image |
|
|
+--------------+-------------+
|
|
|
|
For PARTIAL_UPDATE_ROWS_EVENT:
|
|
+--------------+--------------+-------------+
|
|
| before-image | shared-image | after-image |
|
|
+--------------+--------------+-------------+
|
|
|
|
- Each of before-image and after-image has the following format:
|
|
+--------+-------+-------+ +-------+
|
|
| length | col_1 | col_2 | ... | col_N |
|
|
+--------+-------+-------+ +-------+
|
|
length is a 4-byte integer in little-endian format, equal to the
|
|
total length in bytes of col_1, col_2, ..., col_N.
|
|
|
|
- The shared-image has one of the following formats:
|
|
+-----------------+
|
|
| value_options=0 |
|
|
+-----------------+
|
|
or
|
|
+-----------------+--------------+
|
|
| value_options=1 | partial_bits |
|
|
+-----------------+--------------+
|
|
where:
|
|
|
|
- value_options is a bitmap, stored as an integer, in the format
|
|
of net_field_length. Currently only one bit is allowed:
|
|
1=PARTIAL_JSON_UPDATES (so therefore the integer is always 0 or
|
|
1, so in reality value_options is only one byte). When
|
|
PARTIAL_JSON_UPDATES=0, there is nothing else in the
|
|
shared-image. When PARTIAL_JSON_UPDATES=1, there is a
|
|
partial_bits field.
|
|
|
|
- partial_bits has one bit for each *JSON* column in the table
|
|
(regardless of whether it is included in the before-image and/or
|
|
after-image). The bit is 0 if the JSON update is stored as a
|
|
full document in the after-image, and 1 if the JSON update in
|
|
partial form in the after-image.
|
|
|
|
- Both when reading the before-image and when reading the
|
|
after-image it is necessary to know the partialness of JSON
|
|
columns: when reading the before-image, before looking up the
|
|
row in the table, we need to set the column in the table's
|
|
'read_set' (even if the column was not in the before-image), in
|
|
order to guarantee that the storage engine reads that column, so
|
|
that there is any base document that the diff can be applied
|
|
on. When reading the after-image, we need to know which columns
|
|
are partial so that we can correctly parse the data for that
|
|
column.
|
|
|
|
Therefore, when this function parses the before-image of a
|
|
PARTIAL_UPDATE_ROWS_LOG_EVENT, it reads both the before-image
|
|
and the shared-image, but leaves the read position after the
|
|
before-image. So when it parses the after-image of a
|
|
PARTIAL_UPDATE_ROWS_LOG_EVENT, the read position is at the
|
|
beginning of the shared-image, so it parses both the
|
|
shared-image and the after-image.
|
|
|
|
@param[in] rli Applier execution context
|
|
|
|
@param[in,out] table Table to unpack into
|
|
|
|
@param[in] source_column_count Number of columns that the source had
|
|
in its table
|
|
|
|
@param[in] row_data Beginning of the row image
|
|
|
|
@param[in] column_image Pointer to a bit vector where the N'th bit
|
|
is 0 for columns that are not included in the event, and 1 for
|
|
columns that are included in the event.
|
|
|
|
@param[out] row_image_end_p If this function returns successfully, it
|
|
sets row_image_end to point to the next byte after the row image
|
|
that it has read.
|
|
|
|
@param[in] event_end Pointer to the end of the event.
|
|
|
|
@param[in] row_image_type The type of row image that we are going to
|
|
read: WRITE_AI, UPDATE_BI, UPDATE_AI, or DELETE_BI.
|
|
|
|
@param[in] event_has_value_options true for PARTIAL_UPDATE_ROWS_EVENT,
|
|
false for UPDATE_ROWS_EVENT.
|
|
|
|
@param only_seek If true, this is a seek operation rather than a
|
|
read operation. It will only compute the row_image_end_p pointer,
|
|
and not read anything into the table and not apply any JSON diffs.
|
|
(This is used in HASH_SCAN, which (1) unpacks and hashes the
|
|
before-image for all rows in the event, (2)vscans the table, and
|
|
for each matching row it (3) unpacks the
|
|
after-image and applies on the table. In step (1) it needs to unpack
|
|
the after-image too, in order to move the read position forwards,
|
|
and then it should use only_seek=true. This is an optimization, but
|
|
more importantly, when the after-image contains partial JSON, the
|
|
partial JSON cannot be applied in step (1) since there is no JSON
|
|
document to apply it on.)
|
|
|
|
@returns false on success, true on error.
|
|
*/
|
|
bool unpack_row(Relay_log_info const *rli, TABLE *table,
|
|
uint const source_column_count, uchar const *const row_data,
|
|
MY_BITMAP const *column_image,
|
|
uchar const **const row_image_end_p,
|
|
uchar const *const event_end,
|
|
enum_row_image_type row_image_type,
|
|
bool event_has_value_options, bool only_seek);
|
|
|
|
/**
|
|
Return a pointer within a row event's row data, to the data of the first
|
|
column that exists on the replica.
|
|
|
|
This skips the 'null bits' field, which precedes the column definitions in the
|
|
row image. In case a GIPK exists in the event but not in this replica's table
|
|
definition, it skips the GIPK too.
|
|
|
|
@param raw_data The data received from the source
|
|
@param column_image The column bitmap
|
|
@param column_count The number of column in the image bitmap
|
|
@param null_bits The bits that are null
|
|
@param tabledef The source table definition structure
|
|
@param source_has_gipk If the source table has a GIPK
|
|
@param replica_has_gipk If the replica table has a GIPK
|
|
|
|
@return const uchar* The adjusted point to the source data
|
|
*/
|
|
const uchar *translate_beginning_of_raw_data(
|
|
const uchar *raw_data, MY_BITMAP const *column_image, size_t column_count,
|
|
Bit_reader &null_bits, table_def *tabledef, bool source_has_gipk,
|
|
bool replica_has_gipk);
|
|
|
|
// Fill table's record[0] with default values.
|
|
int prepare_record(TABLE *const table, const MY_BITMAP *cols, const bool check);
|
|
#endif
|
|
|
|
/**
|
|
Template base class of Bit_reader / Bit_writer.
|
|
*/
|
|
template <typename T, typename UT>
|
|
class Bit_stream_base {
|
|
protected:
|
|
/// Pointer to beginning of buffer where bits are read or written.
|
|
T *m_ptr;
|
|
/// Current position in buffer.
|
|
uint m_current_bit;
|
|
|
|
public:
|
|
/**
|
|
Construct a new Bit_stream (either reader or writer).
|
|
@param ptr Pointer where bits will be read or written.
|
|
*/
|
|
Bit_stream_base(T *ptr) : m_ptr(ptr), m_current_bit(0) {}
|
|
|
|
/**
|
|
Set the buffer pointer.
|
|
@param ptr Pointer where bits will be read or written.
|
|
*/
|
|
void set_ptr(T *ptr) { m_ptr = ptr; }
|
|
/**
|
|
Set the buffer pointer, using an unsigned datatype.
|
|
@param ptr Pointer where bits will be read or written.
|
|
*/
|
|
void set_ptr(UT *ptr) { m_ptr = (T *)ptr; }
|
|
|
|
/// @return the current position.
|
|
uint tell() const { return m_current_bit; }
|
|
|
|
/**
|
|
Print all the bits before the current position to the debug trace.
|
|
@param str Descriptive text that will be prefixed before the bit string.
|
|
*/
|
|
void dbug_print(const char *str [[maybe_unused]]) const;
|
|
};
|
|
|
|
/**
|
|
Auxiliary class to write a stream of bits to a memory location.
|
|
|
|
Call set() to write a bit and move the position one bit forward.
|
|
*/
|
|
class Bit_writer : public Bit_stream_base<char, uchar> {
|
|
public:
|
|
Bit_writer(char *ptr = nullptr) : Bit_stream_base<char, uchar>(ptr) {}
|
|
Bit_writer(uchar *ptr) : Bit_writer((char *)ptr) {}
|
|
|
|
/**
|
|
Write the next bit and move the write position one bit forward.
|
|
@param set_to_on If true, set the bit to 1, otherwise set it to 0.
|
|
*/
|
|
void set(bool set_to_on) {
|
|
uint byte = m_current_bit / 8;
|
|
uint bit_within_byte = m_current_bit % 8;
|
|
m_current_bit++;
|
|
if (bit_within_byte == 0)
|
|
m_ptr[byte] = set_to_on ? 1 : 0;
|
|
else if (set_to_on)
|
|
m_ptr[byte] |= 1 << bit_within_byte;
|
|
}
|
|
};
|
|
|
|
/**
|
|
Auxiliary class to read or write a stream of bits to a memory location.
|
|
|
|
Call get() to read a bit and move the position one bit forward.
|
|
*/
|
|
class Bit_reader : public Bit_stream_base<const char, const uchar> {
|
|
public:
|
|
Bit_reader(const char *ptr = nullptr)
|
|
: Bit_stream_base<const char, const uchar>(ptr) {}
|
|
Bit_reader(const uchar *ptr) : Bit_reader((const char *)ptr) {}
|
|
|
|
/**
|
|
Read the next bit and move the read position one bit forward.
|
|
@return true if the bit was 1, false if the bit was 0.
|
|
*/
|
|
bool get() {
|
|
uint byte = m_current_bit / 8;
|
|
uint bit_within_byte = m_current_bit % 8;
|
|
m_current_bit++;
|
|
return (m_ptr[byte] & (1 << bit_within_byte)) != 0;
|
|
}
|
|
};
|
|
|
|
#endif // ifdef RPL_RECORD_H
|