MySQL 8.4.2
Source Code Documentation
rpl_record.h File Reference
#include <stddef.h>
#include <sys/types.h>
#include "my_inttypes.h"

Go to the source code of this file.

Classes

class  Bit_stream_base< T, UT >
 Template base class of Bit_reader / Bit_writer. More...
 
class  Bit_writer
 Auxiliary class to write a stream of bits to a memory location. More...
 
class  Bit_reader
 Auxiliary class to read or write a stream of bits to a memory location. More...
 

Enumerations

enum class  enum_row_image_type { WRITE_AI , UPDATE_BI , UPDATE_AI , DELETE_BI }
 

Functions

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)
 Pack a record of data for a table into a format suitable for the binary log. More...
 
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)
 Unpack a row image (either before-image or after-image) into table->record[0]. More...
 
const uchartranslate_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)
 Return a pointer within a row event's row data, to the data of the first column that exists on the replica. More...
 
int prepare_record (TABLE *const table, const MY_BITMAP *cols, const bool check)
 Fills table->record[0] with default values. More...
 

Enumeration Type Documentation

◆ enum_row_image_type

enum class enum_row_image_type
strong
Enumerator
WRITE_AI 
UPDATE_BI 
UPDATE_AI 
DELETE_BI 

Function Documentation

◆ pack_row()

size_t pack_row ( TABLE table,
MY_BITMAP const *  columns_in_image,
uchar row_data,
const uchar record,
enum_row_image_type  row_image_type,
ulonglong  value_options 
)

Pack a record of data for a table into a format suitable for the binary log.

The format for a row where N columns are included in the image is the following:

+-----------+----------+----------+     +----------+
| null_bits | column_1 | column_2 | ... | column_N |
+-----------+----------+----------+     +----------+

Where

  • null_bits is a bitmap using ceil(N/8) bytes. There is one bit for every column included in the image, regardless of whether it can be null or not. The number of null bits is equal to the number of bits set in the columns_in_image bitmap.
  • column_i: Each of the N columns is stored in a format that depends on the type of the column.
Parameters
tableTable describing the format of the record
columns_in_imageBitmap with a set bit for each column that should be stored in the row.
row_dataPointer to memory where row will be written
recordPointer to record retrieved from the engine.
row_image_typeThe type of image: before-image or after-image, for a Write/Update/Delete event.
value_optionsThe value of @session.binlog_row_value_options
Returns
The number of bytes written at row_data.

◆ prepare_record()

int prepare_record ( TABLE *const  table,
const MY_BITMAP cols,
const bool  check 
)

Fills table->record[0] with default values.

First restore_record() is called to restore the default values for the record concerning the given table. Then, if check is true, a check is performed to see if fields have the default value or can be NULL. Otherwise an error is reported.

Parameters
tableTable whose record[0] buffer is prepared.
colsbitmap with a set bit for each column that should be stored in a row.
checkSpecifies if lack of default error needs checking.
Returns
0 on success or a handler level error code

Save a reference to the original write set bitmaps. We will need this to restore the bitmaps at the end.

Just to be sure that tmp_set is currently not in use as the write_set already.

Set table->write_set bits for all the columns as they will be checked in set_default() function.

◆ translate_beginning_of_raw_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 
)

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.

Parameters
raw_dataThe data received from the source
column_imageThe column bitmap
column_countThe number of column in the image bitmap
null_bitsThe bits that are null
tabledefThe source table definition structure
source_has_gipkIf the source table has a GIPK
replica_has_gipkIf the replica table has a GIPK
Returns
const uchar* The adjusted point to the source data

◆ unpack_row()

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 
)

Unpack a row image (either before-image or after-image) into table->record[0].

The row is assumed to only consist of the fields for which the corresponding bit in bitset 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.

Parameters
[in]rliApplier execution context
[in,out]tableTable to unpack into
[in]source_column_countNumber of columns that the source had in its table
[in]row_dataBeginning of the row image
[in]column_imagePointer 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.
[out]row_image_end_pIf this function returns successfully, it sets row_image_end to point to the next byte after the row image that it has read.
[in]event_endPointer to the end of the event.
[in]row_image_typeThe type of row image that we are going to read: WRITE_AI, UPDATE_BI, UPDATE_AI, or DELETE_BI.
[in]event_has_value_optionstrue for PARTIAL_UPDATE_ROWS_EVENT, false for UPDATE_ROWS_EVENT.
only_seekIf 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.

Calling reset just in case one is unpacking on top a record with data.

This could probably go into set_null() but doing so, (i) triggers assertion in other parts of the code at the moment; (ii) it would make us reset the field, always when setting null, which right now doesn't seem needed anywhere else except here.

TODO: maybe in the future we should consider moving the reset to make it part of set_null. But then the assertions triggered need to be addressed/revisited.