10
10
from .constants import FIELD_TYPE
11
11
from .constants import BINLOG
12
12
from .constants import CHARSET
13
+ from .constants import NONE_SOURCE
13
14
from .column import Column
14
15
from .table import Table
15
16
from .bitmap import BitCount , BitGet
@@ -23,6 +24,7 @@ def __init__(self, from_packet, event_size, table_map, ctl_connection, **kwargs)
23
24
self .__ignored_tables = kwargs ["ignored_tables" ]
24
25
self .__only_schemas = kwargs ["only_schemas" ]
25
26
self .__ignored_schemas = kwargs ["ignored_schemas" ]
27
+ self .__none_sources = {}
26
28
27
29
# Header
28
30
self .table_id = self ._read_table_id ()
@@ -176,10 +178,15 @@ def __read_values_name(
176
178
unsigned ,
177
179
i ,
178
180
):
181
+ name = self .table_map [self .table_id ].columns [i ].name
179
182
if BitGet (cols_bitmap , i ) == 0 :
183
+ # This block is only executed when binlog_row_image = MINIMAL.
184
+ # When binlog_row_image = FULL, this block does not execute.
185
+ self .__none_sources [name ] = NONE_SOURCE .COLS_BITMAP
180
186
return None
181
187
182
188
if self ._is_null (null_bitmap , null_bitmap_index ):
189
+ self .__none_sources [name ] = NONE_SOURCE .NULL
183
190
return None
184
191
185
192
if column .type == FIELD_TYPE .TINY :
@@ -223,17 +230,26 @@ def __read_values_name(
223
230
elif column .type == FIELD_TYPE .BLOB :
224
231
return self .__read_string (column .length_size , column )
225
232
elif column .type == FIELD_TYPE .DATETIME :
226
- return self .__read_datetime ()
233
+ ret = self .__read_datetime ()
234
+ if ret is None :
235
+ self .__none_sources [name ] = NONE_SOURCE .OUT_OF_DATETIME_RANGE
236
+ return ret
227
237
elif column .type == FIELD_TYPE .TIME :
228
238
return self .__read_time ()
229
239
elif column .type == FIELD_TYPE .DATE :
230
- return self .__read_date ()
240
+ ret = self .__read_date ()
241
+ if ret is None :
242
+ self .__none_sources [name ] = NONE_SOURCE .OUT_OF_DATE_RANGE
243
+ return ret
231
244
elif column .type == FIELD_TYPE .TIMESTAMP :
232
245
return datetime .datetime .utcfromtimestamp (self .packet .read_uint32 ())
233
246
234
247
# For new date format:
235
248
elif column .type == FIELD_TYPE .DATETIME2 :
236
- return self .__read_datetime2 (column )
249
+ ret = self .__read_datetime2 (column )
250
+ if ret is None :
251
+ self .__none_sources [name ] = NONE_SOURCE .OUT_OF_DATETIME2_RANGE
252
+ return ret
237
253
elif column .type == FIELD_TYPE .TIME2 :
238
254
return self .__read_time2 (column )
239
255
elif column .type == FIELD_TYPE .TIMESTAMP2 :
@@ -257,11 +273,16 @@ def __read_values_name(
257
273
elif column .type == FIELD_TYPE .SET :
258
274
bit_mask = self .packet .read_uint_by_size (column .size )
259
275
if column .set_values :
260
- return {
276
+ ret = {
261
277
val
262
278
for idx , val in enumerate (column .set_values )
263
279
if bit_mask & (1 << idx )
264
- } or None
280
+ }
281
+ if not ret :
282
+ self .__none_sources [column .name ] = NONE_SOURCE .EMPTY_SET
283
+ return None
284
+ return ret
285
+ self .__none_sources [column .name ] = NONE_SOURCE .EMPTY_SET
265
286
return None
266
287
elif column .type == FIELD_TYPE .BIT :
267
288
return self .__read_bit (column )
@@ -515,6 +536,16 @@ def _json_column_count(self):
515
536
count += 1
516
537
return count
517
538
539
+ def _get_none_sources (self , column_data ):
540
+ result = {}
541
+ for column_name , value in column_data .items ():
542
+ if (column_name is None ) or (value is not None ):
543
+ continue
544
+
545
+ source = self .__none_sources .get (column_name , "null" )
546
+ result [column_name ] = source
547
+ return result
548
+
518
549
def _dump (self ):
519
550
super ()._dump ()
520
551
print (f"Table: { self .schema } .{ self .table } " )
@@ -557,6 +588,8 @@ def _fetch_one_row(self):
557
588
row = {}
558
589
559
590
row ["values" ] = self ._read_column_data (self .columns_present_bitmap )
591
+ row ["none_sources" ] = self ._get_none_sources (row ["values" ])
592
+
560
593
return row
561
594
562
595
def _dump (self ):
@@ -565,7 +598,13 @@ def _dump(self):
565
598
for row in self .rows :
566
599
print ("--" )
567
600
for key in row ["values" ]:
568
- print (f"* { key } : { row ['values' ][key ]} " )
601
+ none_source = (
602
+ row ["none_sources" ][key ] if key in row ["none_sources" ] else ""
603
+ )
604
+ if none_source :
605
+ print (f"* { key } : { row ['values' ][key ]} ({ none_source } )" )
606
+ else :
607
+ print (f"* { key } : { row ['values' ][key ]} " )
569
608
570
609
571
610
class WriteRowsEvent (RowsEvent ):
@@ -585,6 +624,8 @@ def _fetch_one_row(self):
585
624
row = {}
586
625
587
626
row ["values" ] = self ._read_column_data (self .columns_present_bitmap )
627
+ row ["none_sources" ] = self ._get_none_sources (row ["values" ])
628
+
588
629
return row
589
630
590
631
def _dump (self ):
@@ -593,7 +634,13 @@ def _dump(self):
593
634
for row in self .rows :
594
635
print ("--" )
595
636
for key in row ["values" ]:
596
- print (f"* { key } : { row ['values' ][key ]} " )
637
+ none_source = (
638
+ row ["none_sources" ][key ] if key in row ["none_sources" ] else ""
639
+ )
640
+ if none_source :
641
+ print (f"* { key } : row['values'][key] ({ none_source } )" )
642
+ else :
643
+ print (f"* { key } : { row ['values' ][key ]} " )
597
644
598
645
599
646
class UpdateRowsEvent (RowsEvent ):
@@ -623,8 +670,9 @@ def _fetch_one_row(self):
623
670
row = {}
624
671
625
672
row ["before_values" ] = self ._read_column_data (self .columns_present_bitmap )
626
-
673
+ row [ "before_none_sources" ] = self . _get_none_sources ( row [ "before_values" ])
627
674
row ["after_values" ] = self ._read_column_data (self .columns_present_bitmap2 )
675
+ row ["after_none_sources" ] = self ._get_none_sources (row ["after_values" ])
628
676
return row
629
677
630
678
def _dump (self ):
@@ -633,7 +681,23 @@ def _dump(self):
633
681
for row in self .rows :
634
682
print ("--" )
635
683
for key in row ["before_values" ]:
636
- print (f"*{ key } :{ row ['before_values' ][key ]} =>{ row ['after_values' ][key ]} " )
684
+ if key in row ["before_none_sources" ]:
685
+ before_value_info = "%s(%s)" % (
686
+ row ["before_values" ][key ],
687
+ row ["before_none_sources" ][key ],
688
+ )
689
+ else :
690
+ before_value_info = row ["before_values" ][key ]
691
+
692
+ if key in row ["after_none_sources" ]:
693
+ after_value_info = "%s(%s)" % (
694
+ row ["after_values" ][key ],
695
+ row ["after_none_sources" ][key ],
696
+ )
697
+ else :
698
+ after_value_info = row ["after_values" ][key ]
699
+
700
+ print (f"*{ key } :{ before_value_info } =>{ after_value_info } " )
637
701
638
702
639
703
class OptionalMetaData :
0 commit comments