Skip to content

Commit 3688bf8

Browse files
authored
Support for custom types mapping, support for UUID (#97)
1 parent 6410a7b commit 3688bf8

File tree

5 files changed

+33
-4
lines changed

5 files changed

+33
-4
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ indexes: # optional
175175
http_host: '0.0.0.0' # optional
176176
http_port: 9128 # optional
177177

178+
types_mapping: # optional
179+
'char(36)': 'UUID'
180+
181+
178182
```
179183

180184
#### Required settings
@@ -194,6 +198,7 @@ http_port: 9128 # optional
194198
- `auto_restart_interval` - interval (seconds) between automatic db_replicator restart. Default 3600 (1 hour). This is done to reduce memory usage.
195199
- `indexes` - you may want to add some indexes to accelerate performance, eg. ngram index for full-test search, etc. To apply indexes you need to start replication from scratch.
196200
- `http_host`, `http_port` - http endpoint to control replication, use `/docs` for abailable commands
201+
- `types_mappings` - custom types mapping, eg. you can map char(36) to UUID instead of String, etc.
197202

198203
Few more tables / dbs examples:
199204

mysql_ch_replicator/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def __init__(self):
110110
self.auto_restart_interval = 0
111111
self.http_host = ''
112112
self.http_port = 0
113+
self.types_mapping = {}
113114
self.target_databases = {}
114115

115116
def load(self, settings_file):
@@ -131,6 +132,7 @@ def load(self, settings_file):
131132
self.auto_restart_interval = data.pop(
132133
'auto_restart_interval', Settings.DEFAULT_AUTO_RESTART_INTERVAL,
133134
)
135+
self.types_mapping = data.pop('types_mapping', {})
134136
self.http_host = data.pop('http_host', '')
135137
self.http_port = data.pop('http_port', 0)
136138
self.target_databases = data.pop('target_databases', {})

mysql_ch_replicator/converter.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import struct
22
import json
3+
import uuid
34
import sqlparse
45
import re
56
from pyparsing import Suppress, CaselessKeyword, Word, alphas, alphanums, delimitedList
@@ -180,10 +181,17 @@ def convert_timestamp_to_datetime64(input_str):
180181
class MysqlToClickhouseConverter:
181182
def __init__(self, db_replicator: 'DbReplicator' = None):
182183
self.db_replicator = db_replicator
184+
self.types_mapping = {}
185+
if self.db_replicator is not None:
186+
self.types_mapping = db_replicator.config.types_mapping
183187

184188
def convert_type(self, mysql_type, parameters):
185189
is_unsigned = 'unsigned' in parameters.lower()
186190

191+
result_type = self.types_mapping.get(mysql_type)
192+
if result_type is not None:
193+
return result_type
194+
187195
if mysql_type == 'point':
188196
return 'Tuple(x Float32, y Float32)'
189197

@@ -329,6 +337,12 @@ def convert_record(
329337
clickhouse_field_value = json.dumps(convert_bytes(clickhouse_field_value))
330338

331339
if clickhouse_field_value is not None:
340+
if 'UUID' in clickhouse_field_type:
341+
if len(clickhouse_field_value) == 36:
342+
if isinstance(clickhouse_field_value, bytes):
343+
clickhouse_field_value = clickhouse_field_value.decode('utf-8')
344+
clickhouse_field_value = uuid.UUID(clickhouse_field_value).bytes
345+
332346
if 'UInt16' in clickhouse_field_type and clickhouse_field_value < 0:
333347
clickhouse_field_value = 65536 + clickhouse_field_value
334348
if 'UInt8' in clickhouse_field_type and clickhouse_field_value < 0:

test_mysql_ch_replicator.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import time
55
import subprocess
66
import json
7+
import uuid
8+
79
import pytest
810
import requests
911

@@ -976,13 +978,14 @@ def test_different_types_2():
976978
test3 binary(16),
977979
test4 set('1','2','3','4','5','6','7'),
978980
test5 timestamp(0),
981+
test6 char(36),
979982
PRIMARY KEY (id)
980983
);
981984
''')
982985

983986
mysql.execute(
984-
f"INSERT INTO `{TEST_TABLE_NAME}` (test1, test2, test3, test4, test5) VALUES "
985-
f"(0, POINT(10.0, 20.0), 'azaza', '1,3,5', '2023-08-15 14:30:00');",
987+
f"INSERT INTO `{TEST_TABLE_NAME}` (test1, test2, test3, test4, test5, test6) VALUES "
988+
f"(0, POINT(10.0, 20.0), 'azaza', '1,3,5', '2023-08-15 14:30:00', '550e8400-e29b-41d4-a716-446655440000');",
986989
commit=True,
987990
)
988991

@@ -999,8 +1002,8 @@ def test_different_types_2():
9991002
assert_wait(lambda: len(ch.select(TEST_TABLE_NAME)) == 1)
10001003

10011004
mysql.execute(
1002-
f"INSERT INTO `{TEST_TABLE_NAME}` (test1, test2, test4, test5) VALUES "
1003-
f"(1, POINT(15.0, 14.0), '2,4,5', '2023-08-15 14:40:00');",
1005+
f"INSERT INTO `{TEST_TABLE_NAME}` (test1, test2, test4, test5, test6) VALUES "
1006+
f"(1, POINT(15.0, 14.0), '2,4,5', '2023-08-15 14:40:00', '110e6103-e39b-51d4-a716-826755413099');",
10041007
commit=True,
10051008
)
10061009

@@ -1018,6 +1021,8 @@ def test_different_types_2():
10181021
assert isinstance(value, datetime.datetime)
10191022
assert str(value) == '2023-08-15 14:40:00+00:00'
10201023

1024+
assert ch.select(TEST_TABLE_NAME, 'test1=True')[0]['test6'] == uuid.UUID('110e6103-e39b-51d4-a716-826755413099')
1025+
10211026
mysql.execute(
10221027
f"INSERT INTO `{TEST_TABLE_NAME}` (test1, test2) VALUES "
10231028
f"(0, NULL);",

tests_config.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,6 @@ indexes:
3030

3131
http_host: 'localhost'
3232
http_port: 9128
33+
34+
types_mapping:
35+
'char(36)': 'UUID'

0 commit comments

Comments
 (0)