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 ()
@@ -143,10 +145,15 @@ def _read_column_data(self, cols_bitmap):
143
145
def __read_values_name (
144
146
self , column , null_bitmap , null_bitmap_index , cols_bitmap , unsigned , i
145
147
):
148
+ name = self .table_map [self .table_id ].columns [i ].name
146
149
if BitGet (cols_bitmap , i ) == 0 :
150
+ # This block is only executed when binlog_row_image = MINIMAL.
151
+ # When binlog_row_image = FULL, this block does not execute.
152
+ self .__none_sources [name ] = NONE_SOURCE .COLS_BITMAP
147
153
return None
148
154
149
155
if self ._is_null (null_bitmap , null_bitmap_index ):
156
+ self .__none_sources [name ] = NONE_SOURCE .NULL
150
157
return None
151
158
152
159
if column .type == FIELD_TYPE .TINY :
@@ -190,17 +197,26 @@ def __read_values_name(
190
197
elif column .type == FIELD_TYPE .BLOB :
191
198
return self .__read_string (column .length_size , column )
192
199
elif column .type == FIELD_TYPE .DATETIME :
193
- return self .__read_datetime ()
200
+ ret = self .__read_datetime ()
201
+ if ret is None :
202
+ self .__none_sources [name ] = NONE_SOURCE .OUT_OF_DATETIME_RANGE
203
+ return ret
194
204
elif column .type == FIELD_TYPE .TIME :
195
205
return self .__read_time ()
196
206
elif column .type == FIELD_TYPE .DATE :
197
- return self .__read_date ()
207
+ ret = self .__read_date ()
208
+ if ret is None :
209
+ self .__none_sources [name ] = NONE_SOURCE .OUT_OF_DATE_RANGE
210
+ return ret
198
211
elif column .type == FIELD_TYPE .TIMESTAMP :
199
212
return datetime .datetime .utcfromtimestamp (self .packet .read_uint32 ())
200
213
201
214
# For new date format:
202
215
elif column .type == FIELD_TYPE .DATETIME2 :
203
- return self .__read_datetime2 (column )
216
+ ret = self .__read_datetime2 (column )
217
+ if ret is None :
218
+ self .__none_sources [name ] = NONE_SOURCE .OUT_OF_DATETIME2_RANGE
219
+ return ret
204
220
elif column .type == FIELD_TYPE .TIME2 :
205
221
return self .__read_time2 (column )
206
222
elif column .type == FIELD_TYPE .TIMESTAMP2 :
@@ -224,11 +240,16 @@ def __read_values_name(
224
240
elif column .type == FIELD_TYPE .SET :
225
241
bit_mask = self .packet .read_uint_by_size (column .size )
226
242
if column .set_values :
227
- return {
243
+ ret = {
228
244
val
229
245
for idx , val in enumerate (column .set_values )
230
246
if bit_mask & (1 << idx )
231
- } or None
247
+ }
248
+ if not ret :
249
+ self .__none_sources [column .name ] = NONE_SOURCE .EMPTY_SET
250
+ return None
251
+ return ret
252
+ self .__none_sources [column .name ] = NONE_SOURCE .EMPTY_SET
232
253
return None
233
254
elif column .type == FIELD_TYPE .BIT :
234
255
return self .__read_bit (column )
@@ -475,6 +496,16 @@ def __read_binary_slice(self, binary, start, size, data_length):
475
496
mask = (1 << size ) - 1
476
497
return binary & mask
477
498
499
+ def _get_none_sources (self , column_data ):
500
+ result = {}
501
+ for column_name , value in column_data .items ():
502
+ if (column_name is None ) or (value is not None ):
503
+ continue
504
+
505
+ source = self .__none_sources .get (column_name , "null" )
506
+ result [column_name ] = source
507
+ return result
508
+
478
509
def _dump (self ):
479
510
super ()._dump ()
480
511
print (f"Table: { self .schema } .{ self .table } " )
@@ -517,6 +548,8 @@ def _fetch_one_row(self):
517
548
row = {}
518
549
519
550
row ["values" ] = self ._read_column_data (self .columns_present_bitmap )
551
+ row ["none_sources" ] = self ._get_none_sources (row ["values" ])
552
+
520
553
return row
521
554
522
555
def _dump (self ):
@@ -525,7 +558,13 @@ def _dump(self):
525
558
for row in self .rows :
526
559
print ("--" )
527
560
for key in row ["values" ]:
528
- print (f"* { key } : { row ['values' ][key ]} " )
561
+ none_source = (
562
+ row ["none_sources" ][key ] if key in row ["none_sources" ] else ""
563
+ )
564
+ if none_source :
565
+ print (f"* { key } : { row ['values' ][key ]} ({ none_source } )" )
566
+ else :
567
+ print (f"* { key } : { row ['values' ][key ]} " )
529
568
530
569
531
570
class WriteRowsEvent (RowsEvent ):
@@ -545,6 +584,8 @@ def _fetch_one_row(self):
545
584
row = {}
546
585
547
586
row ["values" ] = self ._read_column_data (self .columns_present_bitmap )
587
+ row ["none_sources" ] = self ._get_none_sources (row ["values" ])
588
+
548
589
return row
549
590
550
591
def _dump (self ):
@@ -553,7 +594,13 @@ def _dump(self):
553
594
for row in self .rows :
554
595
print ("--" )
555
596
for key in row ["values" ]:
556
- print (f"* { key } : { row ['values' ][key ]} " )
597
+ none_source = (
598
+ row ["none_sources" ][key ] if key in row ["none_sources" ] else ""
599
+ )
600
+ if none_source :
601
+ print (f"* { key } : row['values'][key] ({ none_source } )" )
602
+ else :
603
+ print (f"* { key } : { row ['values' ][key ]} " )
557
604
558
605
559
606
class UpdateRowsEvent (RowsEvent ):
@@ -583,8 +630,9 @@ def _fetch_one_row(self):
583
630
row = {}
584
631
585
632
row ["before_values" ] = self ._read_column_data (self .columns_present_bitmap )
586
-
633
+ row [ "before_none_sources" ] = self . _get_none_sources ( row [ "before_values" ])
587
634
row ["after_values" ] = self ._read_column_data (self .columns_present_bitmap2 )
635
+ row ["after_none_sources" ] = self ._get_none_sources (row ["after_values" ])
588
636
return row
589
637
590
638
def _dump (self ):
@@ -593,7 +641,23 @@ def _dump(self):
593
641
for row in self .rows :
594
642
print ("--" )
595
643
for key in row ["before_values" ]:
596
- print (f"*{ key } :{ row ['before_values' ][key ]} =>{ row ['after_values' ][key ]} " )
644
+ if key in row ["before_none_sources" ]:
645
+ before_value_info = "%s(%s)" % (
646
+ row ["before_values" ][key ],
647
+ row ["before_none_sources" ][key ],
648
+ )
649
+ else :
650
+ before_value_info = row ["before_values" ][key ]
651
+
652
+ if key in row ["after_none_sources" ]:
653
+ after_value_info = "%s(%s)" % (
654
+ row ["after_values" ][key ],
655
+ row ["after_none_sources" ][key ],
656
+ )
657
+ else :
658
+ after_value_info = row ["after_values" ][key ]
659
+
660
+ print (f"*{ key } :{ before_value_info } =>{ after_value_info } " )
597
661
598
662
599
663
class OptionalMetaData :
0 commit comments