From e86a19fa1216d53a7de91e1161e5bfe63e1cac64 Mon Sep 17 00:00:00 2001 From: Drashti Chhatralia Date: Fri, 17 Apr 2026 22:11:53 +0530 Subject: [PATCH] TAP-MYSQL: Fix MySQL version compatibility issue --- .../tap_mysql/sync_strategies/binlog.py | 6 +- .../tests/unit/sync_strategies/test_binlog.py | 63 ++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/singer-connectors/tap-mysql/tap_mysql/sync_strategies/binlog.py b/singer-connectors/tap-mysql/tap_mysql/sync_strategies/binlog.py index 6a75f334f..edb1a807e 100644 --- a/singer-connectors/tap-mysql/tap_mysql/sync_strategies/binlog.py +++ b/singer-connectors/tap-mysql/tap_mysql/sync_strategies/binlog.py @@ -115,7 +115,11 @@ def verify_gtid_config(mysql_conn: MySQLConnection): def fetch_current_log_file_and_pos(mysql_conn): with connect_with_backoff(mysql_conn) as open_conn: with open_conn.cursor() as cur: - cur.execute("SHOW MASTER STATUS") + try: + cur.execute("SHOW BINARY LOG STATUS") + except pymysql.err.ProgrammingError: + LOGGER.warning("SHOW BINARY LOG STATUS not supported, falling back to SHOW MASTER STATUS") + cur.execute("SHOW MASTER STATUS") result = cur.fetchone() diff --git a/singer-connectors/tap-mysql/tests/unit/sync_strategies/test_binlog.py b/singer-connectors/tap-mysql/tests/unit/sync_strategies/test_binlog.py index c43757e02..b4ec773ab 100644 --- a/singer-connectors/tap-mysql/tests/unit/sync_strategies/test_binlog.py +++ b/singer-connectors/tap-mysql/tests/unit/sync_strategies/test_binlog.py @@ -9,7 +9,7 @@ from unittest import TestCase from unittest.mock import patch, Mock, call, MagicMock -from pymysql import InternalError +from pymysql import InternalError, ProgrammingError from pymysql.cursors import Cursor from pymysqlreplication.constants import FIELD_TYPE from pymysqlreplication.event import RotateEvent, MariadbGtidEvent, GtidEvent @@ -1826,6 +1826,36 @@ def test_fetch_current_log_file_and_pos_success(self, connect_with_backoff): connect_with_backoff.assert_called_with(mysql_con) cur_mock.__enter__.return_value.execute.assert_has_calls( [ + call('SHOW BINARY LOG STATUS'), + ] + ) + + @patch('tap_mysql.sync_strategies.binlog.connect_with_backoff') + def test_fetch_current_log_file_and_pos_success_fallback(self, connect_with_backoff): + mysql_con = MagicMock(spec_set=MySQLConnection).return_value + cur_mock = MagicMock(spec_set=Cursor).return_value + + def execute_side_effect(query): + if query == 'SHOW BINARY LOG STATUS': + raise ProgrammingError(1064, "You have an error in your SQL syntax...") + + cur_mock.__enter__.return_value.execute.side_effect = execute_side_effect + cur_mock.__enter__.return_value.fetchone.side_effect = [ + ['binlog.000033', 345, ''], + ] + + mysql_con.__enter__.return_value.cursor.return_value = cur_mock + + connect_with_backoff.return_value = mysql_con + + result = binlog.fetch_current_log_file_and_pos(mysql_con) + + self.assertEqual(result, ('binlog.000033', 345)) + + connect_with_backoff.assert_called_with(mysql_con) + cur_mock.__enter__.return_value.execute.assert_has_calls( + [ + call('SHOW BINARY LOG STATUS'), call('SHOW MASTER STATUS'), ] ) @@ -1850,6 +1880,37 @@ def test_fetch_current_log_file_and_pos_fail_if_no_result(self, connect_with_bac connect_with_backoff.assert_called_with(mysql_con) cur_mock.__enter__.return_value.execute.assert_has_calls( [ + call('SHOW BINARY LOG STATUS'), + ] + ) + + @patch('tap_mysql.sync_strategies.binlog.connect_with_backoff') + def test_fetch_current_log_file_and_pos_fail_if_no_result_fallback(self, connect_with_backoff): + mysql_con = MagicMock(spec_set=MySQLConnection).return_value + cur_mock = MagicMock(spec_set=Cursor).return_value + + def execute_side_effect(query): + if query == 'SHOW BINARY LOG STATUS': + raise ProgrammingError(1064, "You have an error in your SQL syntax...") + + cur_mock.__enter__.return_value.execute.side_effect = execute_side_effect + cur_mock.__enter__.return_value.fetchone.side_effect = [ + None + ] + + mysql_con.__enter__.return_value.cursor.return_value = cur_mock + + connect_with_backoff.return_value = mysql_con + + with self.assertRaises(Exception) as context: + binlog.fetch_current_log_file_and_pos(mysql_con) + + self.assertEqual('MySQL binary logging is not enabled.', str(context.exception)) + + connect_with_backoff.assert_called_with(mysql_con) + cur_mock.__enter__.return_value.execute.assert_has_calls( + [ + call('SHOW BINARY LOG STATUS'), call('SHOW MASTER STATUS'), ] )