-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathrpl_record.h
297 lines (242 loc) · 10.9 KB
/
rpl_record.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/* Copyright (c) 2007, 2022, 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 also distributed 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 included with MySQL.
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 slave_rows_search_algorithms=HASH_SCAN, which (1)
unpacks and hashes the before-image for all rows in the event, (2)
scans 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