From 9586be699543a0535568132c490191df0a7cf761 Mon Sep 17 00:00:00 2001 From: siddontang Date: Wed, 17 Dec 2014 16:44:58 +0800 Subject: [PATCH 01/14] add binlog row minimal and noblob image support --- pymysqlreplication/bitmap.py | 75 +++++++++++++++++++++++++++++++++ pymysqlreplication/row_event.py | 36 +++++++++++----- 2 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 pymysqlreplication/bitmap.py diff --git a/pymysqlreplication/bitmap.py b/pymysqlreplication/bitmap.py new file mode 100644 index 00000000..986ec0a8 --- /dev/null +++ b/pymysqlreplication/bitmap.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +import struct + +class Bitmap(object): + """ Use for current-present-bitmap1/2 in RowsEvent, copy from MySQL mysys/my_bitmap.c + """ + + bits2Nbits = [ + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, + ] + + def __init__(self, bitmap, nbits): + self._bitmap = bytearray(((nbits + 31) / 32) * 4) + self._nbits = nbits + self._bitmap[0:len(bitmap)] = bitmap + self._lastWordMask = 0 + self._createLastWordMask() + + def _countBitsUint32(self, v): + s = bytearray(struct.pack(" Date: Wed, 17 Dec 2014 16:59:39 +0800 Subject: [PATCH 02/14] add some explanation for using bitmap --- pymysqlreplication/bitmap.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pymysqlreplication/bitmap.py b/pymysqlreplication/bitmap.py index 986ec0a8..5db846e3 100644 --- a/pymysqlreplication/bitmap.py +++ b/pymysqlreplication/bitmap.py @@ -4,7 +4,14 @@ class Bitmap(object): """ Use for current-present-bitmap1/2 in RowsEvent, copy from MySQL mysys/my_bitmap.c - """ + See http://dev.mysql.com/doc/internals/en/rows-event.html + + null bitmap length = (bits set in 'columns-present-bitmap1'+7)/8 + null bitmap2 length = (bits set in 'columns-present-bitmap2'+7)/8 + + We can not calculate the length using column count directly in minimal or noblob binlog row image. + + """ bits2Nbits = [ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, From 9f001d273c05a4501f1f395fa3e209b94d45ba94 Mon Sep 17 00:00:00 2001 From: siddontang Date: Sun, 21 Dec 2014 10:18:27 +0800 Subject: [PATCH 03/14] try to fix float object is not iterable --- pymysqlreplication/bitmap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pymysqlreplication/bitmap.py b/pymysqlreplication/bitmap.py index 5db846e3..0b8c48a2 100644 --- a/pymysqlreplication/bitmap.py +++ b/pymysqlreplication/bitmap.py @@ -33,8 +33,8 @@ class Bitmap(object): ] def __init__(self, bitmap, nbits): - self._bitmap = bytearray(((nbits + 31) / 32) * 4) - self._nbits = nbits + self._nbits = int(nbits) + self._bitmap = bytearray(((self._nbits + 31) / 32) * 4) self._bitmap[0:len(bitmap)] = bitmap self._lastWordMask = 0 self._createLastWordMask() From 9af972969f4c1a63e9927fe902616d3cf2babb41 Mon Sep 17 00:00:00 2001 From: siddontang Date: Sun, 21 Dec 2014 10:32:23 +0800 Subject: [PATCH 04/14] try fix again --- pymysqlreplication/bitmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymysqlreplication/bitmap.py b/pymysqlreplication/bitmap.py index 0b8c48a2..f0f2e20c 100644 --- a/pymysqlreplication/bitmap.py +++ b/pymysqlreplication/bitmap.py @@ -34,7 +34,7 @@ class Bitmap(object): def __init__(self, bitmap, nbits): self._nbits = int(nbits) - self._bitmap = bytearray(((self._nbits + 31) / 32) * 4) + self._bitmap = bytearray(b'\x00' * int((self._nbits + 31) / 32) * 4) self._bitmap[0:len(bitmap)] = bitmap self._lastWordMask = 0 self._createLastWordMask() From 50fc271530ee320877ed3d5a468a299b4b9d5148 Mon Sep 17 00:00:00 2001 From: siddontang Date: Sun, 21 Dec 2014 17:08:03 +0800 Subject: [PATCH 05/14] try fix again TypeError: unsupported operand type(s) for &: 'float' and 'int' --- pymysqlreplication/bitmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymysqlreplication/bitmap.py b/pymysqlreplication/bitmap.py index f0f2e20c..2d85859e 100644 --- a/pymysqlreplication/bitmap.py +++ b/pymysqlreplication/bitmap.py @@ -47,7 +47,7 @@ def _createLastWordMask(self): used = 1 + (self._nbits - 1) & 0x7 mask = (~(1 << used) - 1) & 255 - l = ((self._nbits + 7) / 8) & 3 + l = int((self._nbits + 7) / 8) & 3 if l == 1: self._lastWordMask = ~0 & 0xFFFFFFFF b = bytearray(struct.pack(" Date: Sun, 21 Dec 2014 19:31:17 +0800 Subject: [PATCH 06/14] try fix, I hate python's dynamic type TypeError: 'float' object cannot be interpreted as an integer --- pymysqlreplication/bitmap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pymysqlreplication/bitmap.py b/pymysqlreplication/bitmap.py index 2d85859e..bea78d53 100644 --- a/pymysqlreplication/bitmap.py +++ b/pymysqlreplication/bitmap.py @@ -45,9 +45,9 @@ def _countBitsUint32(self, v): def _createLastWordMask(self): used = 1 + (self._nbits - 1) & 0x7 - mask = (~(1 << used) - 1) & 255 + mask = (~(1 << used) - 1) & 0xFF - l = int((self._nbits + 7) / 8) & 3 + l = int((self._nbits + 7) / 8) & 0x3 if l == 1: self._lastWordMask = ~0 & 0xFFFFFFFF b = bytearray(struct.pack(" Date: Sun, 21 Dec 2014 22:26:32 +0800 Subject: [PATCH 07/14] try fix error TypeError: bytearray indices must be integers --- pymysqlreplication/bitmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymysqlreplication/bitmap.py b/pymysqlreplication/bitmap.py index bea78d53..e2ba2ed4 100644 --- a/pymysqlreplication/bitmap.py +++ b/pymysqlreplication/bitmap.py @@ -79,4 +79,4 @@ def bits_set(self): return res def is_set(self, bit): - return self._bitmap[bit / 8] & (1 << (bit & 7)) + return self._bitmap[int(bit / 8)] & (1 << (bit & 7)) From facd62ce9702eb0de9a34714f5447b2f23d49aad Mon Sep 17 00:00:00 2001 From: siddontang Date: Sun, 21 Dec 2014 22:53:03 +0800 Subject: [PATCH 08/14] try fix error: unpack requires a string argument of length 4 --- pymysqlreplication/bitmap.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pymysqlreplication/bitmap.py b/pymysqlreplication/bitmap.py index e2ba2ed4..1b75c2b0 100644 --- a/pymysqlreplication/bitmap.py +++ b/pymysqlreplication/bitmap.py @@ -47,6 +47,7 @@ def _createLastWordMask(self): used = 1 + (self._nbits - 1) & 0x7 mask = (~(1 << used) - 1) & 0xFF + b = bytearray(0) l = int((self._nbits + 7) / 8) & 0x3 if l == 1: self._lastWordMask = ~0 & 0xFFFFFFFF From b59ebc7d5e62fd2e5a37f0332b1f5d1ba69e9d59 Mon Sep 17 00:00:00 2001 From: siddontang Date: Mon, 22 Dec 2014 08:45:14 +0800 Subject: [PATCH 09/14] try fix error: unpack requires a string argument of length 4 --- pymysqlreplication/bitmap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pymysqlreplication/bitmap.py b/pymysqlreplication/bitmap.py index 1b75c2b0..549c3ea8 100644 --- a/pymysqlreplication/bitmap.py +++ b/pymysqlreplication/bitmap.py @@ -68,14 +68,14 @@ def _createLastWordMask(self): b = bytearray(struct.pack(" Date: Wed, 24 Dec 2014 10:00:12 +0800 Subject: [PATCH 10/14] remove confused comment --- pymysqlreplication/bitmap.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/pymysqlreplication/bitmap.py b/pymysqlreplication/bitmap.py index 549c3ea8..ddcbf9e6 100644 --- a/pymysqlreplication/bitmap.py +++ b/pymysqlreplication/bitmap.py @@ -3,14 +3,7 @@ import struct class Bitmap(object): - """ Use for current-present-bitmap1/2 in RowsEvent, copy from MySQL mysys/my_bitmap.c - See http://dev.mysql.com/doc/internals/en/rows-event.html - - null bitmap length = (bits set in 'columns-present-bitmap1'+7)/8 - null bitmap2 length = (bits set in 'columns-present-bitmap2'+7)/8 - - We can not calculate the length using column count directly in minimal or noblob binlog row image. - + """ Use for current-present-bitmap1/2 in RowsEvent, refer to mysys/my_bitmap.c """ bits2Nbits = [ From c8cfca83ddb33d9b6aa36a90355eeaad1fabcefe Mon Sep 17 00:00:00 2001 From: siddontang Date: Wed, 24 Dec 2014 10:00:57 +0800 Subject: [PATCH 11/14] variable b renamed to cols_bitmap --- pymysqlreplication/row_event.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/pymysqlreplication/row_event.py b/pymysqlreplication/row_event.py index 1feff4a7..98c128c3 100644 --- a/pymysqlreplication/row_event.py +++ b/pymysqlreplication/row_event.py @@ -61,13 +61,15 @@ def __is_null(self, null_bitmap, position): bit = ord(bit) return bit & (1 << (position % 8)) - def _read_column_data(self, bitmap): + def _read_column_data(self, cols_bitmap): """Use for WRITE, UPDATE and DELETE events. Return an array of column data """ values = {} - null_bitmap = self.packet.read((bitmap.bits_set() + 7) / 8) + # null bitmap length = (bits set in 'columns-present-bitmap'+7)/8 + # See http://dev.mysql.com/doc/internals/en/rows-event.html + null_bitmap = self.packet.read((cols_bitmap.bits_set() + 7) / 8) nullBitmapIndex = 0 nb_columns = len(self.columns) @@ -76,7 +78,7 @@ def _read_column_data(self, bitmap): name = self.table_map[self.table_id].columns[i].name unsigned = self.table_map[self.table_id].columns[i].unsigned - if bitmap.is_set(i) == 0: + if cols_bitmap.is_set(i) == 0: values[name] = None continue @@ -406,9 +408,8 @@ def __init__(self, from_packet, event_size, table_map, ctl_connection, **kwargs) def _fetch_one_row(self): row = {} - b = Bitmap(self.columns_present_bitmap, self.number_of_columns) - #null_bitmap = self.packet.read((self.number_of_columns + 7) / 8) - row["values"] = self._read_column_data(b) + cols_bitmap = Bitmap(self.columns_present_bitmap, self.number_of_columns) + row["values"] = self._read_column_data(cols_bitmap) return row def _dump(self): @@ -436,9 +437,8 @@ def __init__(self, from_packet, event_size, table_map, ctl_connection, **kwargs) def _fetch_one_row(self): row = {} - b = Bitmap(self.columns_present_bitmap, self.number_of_columns) -# null_bitmap = self.packet.read((self.number_of_columns + 7) / 8) - row["values"] = self._read_column_data(b) + cols_bitmap = Bitmap(self.columns_present_bitmap, self.number_of_columns) + row["values"] = self._read_column_data(cols_bitmap) return row def _dump(self): @@ -474,13 +474,11 @@ def __init__(self, from_packet, event_size, table_map, ctl_connection, **kwargs) def _fetch_one_row(self): row = {} - b = Bitmap(self.columns_present_bitmap, self.number_of_columns) - #null_bitmap = self.packet.read((self.number_of_columns + 7) / 8) - row["before_values"] = self._read_column_data(b) + cols_bitmap1 = Bitmap(self.columns_present_bitmap, self.number_of_columns) + row["before_values"] = self._read_column_data(cols_bitmap1) - b = Bitmap(self.columns_present_bitmap2, self.number_of_columns) - #null_bitmap = self.packet.read((self.number_of_columns + 7) / 8) - row["after_values"] = self._read_column_data(b) + cols_bitmap2 = Bitmap(self.columns_present_bitmap2, self.number_of_columns) + row["after_values"] = self._read_column_data(cols_bitmap2) return row def _dump(self): From 3ede586eae4d9d77278cf71246044e6509d58a39 Mon Sep 17 00:00:00 2001 From: siddontang Date: Wed, 24 Dec 2014 12:03:46 +0800 Subject: [PATCH 12/14] use simple bit operations instead not use mysql bitmap --- pymysqlreplication/bitmap.py | 109 ++++++++++---------------------- pymysqlreplication/row_event.py | 19 ++---- 2 files changed, 42 insertions(+), 86 deletions(-) diff --git a/pymysqlreplication/bitmap.py b/pymysqlreplication/bitmap.py index ddcbf9e6..740894ac 100644 --- a/pymysqlreplication/bitmap.py +++ b/pymysqlreplication/bitmap.py @@ -1,76 +1,37 @@ # -*- coding: utf-8 -*- -import struct - -class Bitmap(object): - """ Use for current-present-bitmap1/2 in RowsEvent, refer to mysys/my_bitmap.c - """ - - bits2Nbits = [ - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, - ] - - def __init__(self, bitmap, nbits): - self._nbits = int(nbits) - self._bitmap = bytearray(b'\x00' * int((self._nbits + 31) / 32) * 4) - self._bitmap[0:len(bitmap)] = bitmap - self._lastWordMask = 0 - self._createLastWordMask() - - def _countBitsUint32(self, v): - s = bytearray(struct.pack(" Date: Wed, 24 Dec 2014 13:22:26 +0800 Subject: [PATCH 13/14] add minimal row image test --- pymysqlreplication/tests/test_basic.py | 94 ++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/pymysqlreplication/tests/test_basic.py b/pymysqlreplication/tests/test_basic.py index 770a3a92..1a2227e1 100644 --- a/pymysqlreplication/tests/test_basic.py +++ b/pymysqlreplication/tests/test_basic.py @@ -221,6 +221,100 @@ def test_update_row_event(self): self.assertEqual(event.rows[0]["after_values"]["id"], 1) self.assertEqual(event.rows[0]["after_values"]["data"], "World") + def test_minimal_image_write_row_event(self): + query = "CREATE TABLE test (id INT NOT NULL AUTO_INCREMENT, data VARCHAR (50) NOT NULL, PRIMARY KEY (id))" + self.execute(query) + query = "SET SESSION binlog_row_image = 'minimal'" + self.execute(query) + query = "INSERT INTO test (data) VALUES('Hello World')" + self.execute(query) + self.execute("COMMIT") + + self.assertIsInstance(self.stream.fetchone(), RotateEvent) + self.assertIsInstance(self.stream.fetchone(), FormatDescriptionEvent) + #QueryEvent for the Create Table + self.assertIsInstance(self.stream.fetchone(), QueryEvent) + #QueryEvent for the Set session binlog row image + self.assertIsInstance(self.stream.fetchone(), QueryEvent) + #QueryEvent for the BEGIN + self.assertIsInstance(self.stream.fetchone(), QueryEvent) + + self.assertIsInstance(self.stream.fetchone(), TableMapEvent) + + event = self.stream.fetchone() + if self.isMySQL56AndMore(): + self.assertEqual(event.event_type, WRITE_ROWS_EVENT_V2) + else: + self.assertEqual(event.event_type, WRITE_ROWS_EVENT_V1) + self.assertIsInstance(event, WriteRowsEvent) + self.assertEqual(event.rows[0]["values"]["id"], 1) + self.assertEqual(event.rows[0]["values"]["data"], "Hello World") + self.assertEqual(event.schema, "pymysqlreplication_test") + self.assertEqual(event.table, "test") + self.assertEqual(event.columns[1].name, 'data') + + def test_minimal_image_delete_row_event(self): + query = "CREATE TABLE test (id INT NOT NULL AUTO_INCREMENT, data VARCHAR (50) NOT NULL, PRIMARY KEY (id))" + self.execute(query) + query = "INSERT INTO test (data) VALUES('Hello World')" + self.execute(query) + query = "SET SESSION binlog_row_image = 'minimal'" + self.execute(query) + self.resetBinLog() + + query = "DELETE FROM test WHERE id = 1" + self.execute(query) + self.execute("COMMIT") + + self.assertIsInstance(self.stream.fetchone(), RotateEvent) + self.assertIsInstance(self.stream.fetchone(), FormatDescriptionEvent) + + #QueryEvent for the BEGIN + self.assertIsInstance(self.stream.fetchone(), QueryEvent) + + self.assertIsInstance(self.stream.fetchone(), TableMapEvent) + + event = self.stream.fetchone() + if self.isMySQL56AndMore(): + self.assertEqual(event.event_type, DELETE_ROWS_EVENT_V2) + else: + self.assertEqual(event.event_type, DELETE_ROWS_EVENT_V1) + self.assertIsInstance(event, DeleteRowsEvent) + self.assertEqual(event.rows[0]["values"]["id"], 1) + self.assertEqual(event.rows[0]["values"]["data"], None) + + def test_minimal_image_update_row_event(self): + query = "CREATE TABLE test (id INT NOT NULL AUTO_INCREMENT, data VARCHAR (50) NOT NULL, PRIMARY KEY (id))" + self.execute(query) + query = "INSERT INTO test (data) VALUES('Hello')" + self.execute(query) + query = "SET SESSION binlog_row_image = 'minimal'" + self.execute(query) + self.resetBinLog() + + query = "UPDATE test SET data = 'World' WHERE id = 1" + self.execute(query) + self.execute("COMMIT") + + self.assertIsInstance(self.stream.fetchone(), RotateEvent) + self.assertIsInstance(self.stream.fetchone(), FormatDescriptionEvent) + + #QueryEvent for the BEGIN + self.assertIsInstance(self.stream.fetchone(), QueryEvent) + + self.assertIsInstance(self.stream.fetchone(), TableMapEvent) + + event = self.stream.fetchone() + if self.isMySQL56AndMore(): + self.assertEqual(event.event_type, UPDATE_ROWS_EVENT_V2) + else: + self.assertEqual(event.event_type, UPDATE_ROWS_EVENT_V1) + self.assertIsInstance(event, UpdateRowsEvent) + self.assertEqual(event.rows[0]["before_values"]["id"], 1) + self.assertEqual(event.rows[0]["before_values"]["data"], "Hello") + self.assertEqual(event.rows[0]["after_values"]["id"], None) + self.assertEqual(event.rows[0]["after_values"]["data"], "World") + def test_log_pos(self): query = "CREATE TABLE test (id INT NOT NULL AUTO_INCREMENT, data VARCHAR (50) NOT NULL, PRIMARY KEY (id))" self.execute(query) From bdfc90f6b8e83824fc7d941ab9d6b79ab1906412 Mon Sep 17 00:00:00 2001 From: siddontang Date: Wed, 24 Dec 2014 13:30:22 +0800 Subject: [PATCH 14/14] fix test error --- pymysqlreplication/tests/test_basic.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pymysqlreplication/tests/test_basic.py b/pymysqlreplication/tests/test_basic.py index 1a2227e1..ac804aae 100644 --- a/pymysqlreplication/tests/test_basic.py +++ b/pymysqlreplication/tests/test_basic.py @@ -234,8 +234,6 @@ def test_minimal_image_write_row_event(self): self.assertIsInstance(self.stream.fetchone(), FormatDescriptionEvent) #QueryEvent for the Create Table self.assertIsInstance(self.stream.fetchone(), QueryEvent) - #QueryEvent for the Set session binlog row image - self.assertIsInstance(self.stream.fetchone(), QueryEvent) #QueryEvent for the BEGIN self.assertIsInstance(self.stream.fetchone(), QueryEvent) @@ -311,7 +309,7 @@ def test_minimal_image_update_row_event(self): self.assertEqual(event.event_type, UPDATE_ROWS_EVENT_V1) self.assertIsInstance(event, UpdateRowsEvent) self.assertEqual(event.rows[0]["before_values"]["id"], 1) - self.assertEqual(event.rows[0]["before_values"]["data"], "Hello") + self.assertEqual(event.rows[0]["before_values"]["data"], None) self.assertEqual(event.rows[0]["after_values"]["id"], None) self.assertEqual(event.rows[0]["after_values"]["data"], "World")