Skip to content

Commit a0e1965

Browse files
authored
Support for year type (#130)
1 parent c732930 commit a0e1965

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

mysql_ch_replicator/converter.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ def convert_type(self, mysql_type, parameters):
381381
return 'String'
382382
if 'set(' in mysql_type:
383383
return 'String'
384+
if mysql_type == 'year':
385+
return 'UInt16' # MySQL YEAR type can store years from 1901 to 2155, UInt16 is sufficient
384386
raise Exception(f'unknown mysql type "{mysql_type}"')
385387

386388
def convert_field_type(self, mysql_type, mysql_parameters):
@@ -498,6 +500,18 @@ def convert_record(
498500
field_name
499501
)
500502

503+
# Handle MySQL YEAR type conversion
504+
if mysql_field_type == 'year' and clickhouse_field_value is not None:
505+
# MySQL YEAR type can store years from 1901 to 2155
506+
# Convert to integer if it's a string
507+
if isinstance(clickhouse_field_value, str):
508+
clickhouse_field_value = int(clickhouse_field_value)
509+
# Ensure the value is within valid range
510+
if clickhouse_field_value < 1901:
511+
clickhouse_field_value = 1901
512+
elif clickhouse_field_value > 2155:
513+
clickhouse_field_value = 2155
514+
501515
clickhouse_record.append(clickhouse_field_value)
502516
return tuple(clickhouse_record)
503517

test_mysql_ch_replicator.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,3 +1955,91 @@ def test_create_table_like():
19551955
# Clean up
19561956
db_replicator_runner.stop()
19571957
binlog_replicator_runner.stop()
1958+
1959+
1960+
def test_year_type():
1961+
"""
1962+
Test that MySQL YEAR type is properly converted to UInt16 in ClickHouse
1963+
and that year values are correctly handled.
1964+
"""
1965+
config_file = CONFIG_FILE
1966+
cfg = config.Settings()
1967+
cfg.load(config_file)
1968+
mysql_config = cfg.mysql
1969+
clickhouse_config = cfg.clickhouse
1970+
mysql = mysql_api.MySQLApi(
1971+
database=None,
1972+
mysql_settings=mysql_config
1973+
)
1974+
ch = clickhouse_api.ClickhouseApi(
1975+
database=TEST_DB_NAME,
1976+
clickhouse_settings=clickhouse_config
1977+
)
1978+
1979+
prepare_env(cfg, mysql, ch)
1980+
1981+
mysql.execute(f'''
1982+
CREATE TABLE `{TEST_TABLE_NAME}` (
1983+
id INT NOT NULL AUTO_INCREMENT,
1984+
year_field YEAR NOT NULL,
1985+
nullable_year YEAR,
1986+
PRIMARY KEY (id)
1987+
)
1988+
''')
1989+
1990+
# Insert test data with various year values
1991+
mysql.execute(f'''
1992+
INSERT INTO `{TEST_TABLE_NAME}` (year_field, nullable_year) VALUES
1993+
(2024, 2024),
1994+
(1901, NULL),
1995+
(2155, 2000),
1996+
(2000, 1999);
1997+
''', commit=True)
1998+
1999+
run_all_runner = RunAllRunner(cfg_file=config_file)
2000+
run_all_runner.run()
2001+
2002+
assert_wait(lambda: TEST_DB_NAME in ch.get_databases())
2003+
ch.execute_command(f'USE `{TEST_DB_NAME}`')
2004+
assert_wait(lambda: TEST_TABLE_NAME in ch.get_tables())
2005+
assert_wait(lambda: len(ch.select(TEST_TABLE_NAME)) == 4)
2006+
2007+
# Get the ClickHouse data
2008+
results = ch.select(TEST_TABLE_NAME)
2009+
2010+
# Verify the data
2011+
assert results[0]['year_field'] == 2024
2012+
assert results[0]['nullable_year'] == 2024
2013+
assert results[1]['year_field'] == 1901
2014+
assert results[1]['nullable_year'] is None
2015+
assert results[2]['year_field'] == 2155
2016+
assert results[2]['nullable_year'] == 2000
2017+
assert results[3]['year_field'] == 2000
2018+
assert results[3]['nullable_year'] == 1999
2019+
2020+
# Test realtime replication by adding more records
2021+
mysql.execute(f'''
2022+
INSERT INTO `{TEST_TABLE_NAME}` (year_field, nullable_year) VALUES
2023+
(2025, 2025),
2024+
(1999, NULL),
2025+
(2100, 2100);
2026+
''', commit=True)
2027+
2028+
# Wait for new records to be replicated
2029+
assert_wait(lambda: len(ch.select(TEST_TABLE_NAME)) == 7)
2030+
2031+
# Verify the new records - include order by in the where clause
2032+
new_results = ch.select(TEST_TABLE_NAME, where="year_field >= 2025 ORDER BY year_field ASC")
2033+
assert len(new_results) == 3
2034+
2035+
# Check specific values
2036+
assert new_results[0]['year_field'] == 2025
2037+
assert new_results[0]['nullable_year'] == 2025
2038+
assert new_results[1]['year_field'] == 2100
2039+
assert new_results[1]['nullable_year'] == 2100
2040+
assert new_results[2]['year_field'] == 2155
2041+
assert new_results[2]['nullable_year'] == 2000
2042+
2043+
run_all_runner.stop()
2044+
assert_wait(lambda: 'stopping db_replicator' in read_logs(TEST_DB_NAME))
2045+
assert('Traceback' not in read_logs(TEST_DB_NAME))

0 commit comments

Comments
 (0)