1010from .constants import FIELD_TYPE
1111from .constants import BINLOG
1212from .constants import CHARSET
13+ from .constants import NONE_SOURCE
1314from .column import Column
1415from .table import Table
1516from .bitmap import BitCount , BitGet
@@ -23,6 +24,7 @@ def __init__(self, from_packet, event_size, table_map, ctl_connection, **kwargs)
2324 self .__ignored_tables = kwargs ["ignored_tables" ]
2425 self .__only_schemas = kwargs ["only_schemas" ]
2526 self .__ignored_schemas = kwargs ["ignored_schemas" ]
27+ self .__none_sources = {}
2628
2729 # Header
2830 self .table_id = self ._read_table_id ()
@@ -143,10 +145,15 @@ def _read_column_data(self, cols_bitmap):
143145 def __read_values_name (
144146 self , column , null_bitmap , null_bitmap_index , cols_bitmap , unsigned , i
145147 ):
148+ name = self .table_map [self .table_id ].columns [i ].name
146149 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
147153 return None
148154
149155 if self ._is_null (null_bitmap , null_bitmap_index ):
156+ self .__none_sources [name ] = NONE_SOURCE .NULL
150157 return None
151158
152159 if column .type == FIELD_TYPE .TINY :
@@ -190,17 +197,26 @@ def __read_values_name(
190197 elif column .type == FIELD_TYPE .BLOB :
191198 return self .__read_string (column .length_size , column )
192199 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
194204 elif column .type == FIELD_TYPE .TIME :
195205 return self .__read_time ()
196206 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
198211 elif column .type == FIELD_TYPE .TIMESTAMP :
199212 return datetime .datetime .utcfromtimestamp (self .packet .read_uint32 ())
200213
201214 # For new date format:
202215 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
204220 elif column .type == FIELD_TYPE .TIME2 :
205221 return self .__read_time2 (column )
206222 elif column .type == FIELD_TYPE .TIMESTAMP2 :
@@ -224,11 +240,16 @@ def __read_values_name(
224240 elif column .type == FIELD_TYPE .SET :
225241 bit_mask = self .packet .read_uint_by_size (column .size )
226242 if column .set_values :
227- return {
243+ ret = {
228244 val
229245 for idx , val in enumerate (column .set_values )
230246 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
232253 return None
233254 elif column .type == FIELD_TYPE .BIT :
234255 return self .__read_bit (column )
@@ -475,6 +496,16 @@ def __read_binary_slice(self, binary, start, size, data_length):
475496 mask = (1 << size ) - 1
476497 return binary & mask
477498
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+
478509 def _dump (self ):
479510 super ()._dump ()
480511 print (f"Table: { self .schema } .{ self .table } " )
@@ -517,6 +548,8 @@ def _fetch_one_row(self):
517548 row = {}
518549
519550 row ["values" ] = self ._read_column_data (self .columns_present_bitmap )
551+ row ["none_sources" ] = self ._get_none_sources (row ["values" ])
552+
520553 return row
521554
522555 def _dump (self ):
@@ -525,7 +558,13 @@ def _dump(self):
525558 for row in self .rows :
526559 print ("--" )
527560 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 ]} " )
529568
530569
531570class WriteRowsEvent (RowsEvent ):
@@ -545,6 +584,8 @@ def _fetch_one_row(self):
545584 row = {}
546585
547586 row ["values" ] = self ._read_column_data (self .columns_present_bitmap )
587+ row ["none_sources" ] = self ._get_none_sources (row ["values" ])
588+
548589 return row
549590
550591 def _dump (self ):
@@ -553,7 +594,13 @@ def _dump(self):
553594 for row in self .rows :
554595 print ("--" )
555596 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 ]} " )
557604
558605
559606class UpdateRowsEvent (RowsEvent ):
@@ -583,8 +630,9 @@ def _fetch_one_row(self):
583630 row = {}
584631
585632 row ["before_values" ] = self ._read_column_data (self .columns_present_bitmap )
586-
633+ row [ "before_none_sources" ] = self . _get_none_sources ( row [ "before_values" ])
587634 row ["after_values" ] = self ._read_column_data (self .columns_present_bitmap2 )
635+ row ["after_none_sources" ] = self ._get_none_sources (row ["after_values" ])
588636 return row
589637
590638 def _dump (self ):
@@ -593,7 +641,23 @@ def _dump(self):
593641 for row in self .rows :
594642 print ("--" )
595643 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 } " )
597661
598662
599663class OptionalMetaData :
0 commit comments