1+ import struct
12import json
23import sqlparse
34import re
@@ -27,6 +28,58 @@ def convert_bytes(obj):
2728 return obj
2829
2930
31+ def parse_mysql_point (binary ):
32+ """
33+ Parses the binary representation of a MySQL POINT data type
34+ and returns a tuple (x, y) representing the coordinates.
35+
36+ :param binary: The binary data representing the POINT.
37+ :return: A tuple (x, y) with the coordinate values.
38+ """
39+ if binary is None :
40+ return 0 , 0
41+
42+ if len (binary ) == 21 :
43+ # No SRID. Proceed as per WKB POINT
44+ # Read the byte order
45+ byte_order = binary [0 ]
46+ if byte_order == 0 :
47+ endian = '>'
48+ elif byte_order == 1 :
49+ endian = '<'
50+ else :
51+ raise ValueError ("Invalid byte order in WKB POINT" )
52+ # Read the WKB Type
53+ wkb_type = struct .unpack (endian + 'I' , binary [1 :5 ])[0 ]
54+ if wkb_type != 1 : # WKB type 1 means POINT
55+ raise ValueError ("Not a WKB POINT type" )
56+ # Read X and Y coordinates
57+ x = struct .unpack (endian + 'd' , binary [5 :13 ])[0 ]
58+ y = struct .unpack (endian + 'd' , binary [13 :21 ])[0 ]
59+ elif len (binary ) == 25 :
60+ # With SRID included
61+ # First 4 bytes are the SRID
62+ srid = struct .unpack ('>I' , binary [0 :4 ])[0 ] # SRID is big-endian
63+ # Next byte is byte order
64+ byte_order = binary [4 ]
65+ if byte_order == 0 :
66+ endian = '>'
67+ elif byte_order == 1 :
68+ endian = '<'
69+ else :
70+ raise ValueError ("Invalid byte order in WKB POINT" )
71+ # Read the WKB Type
72+ wkb_type = struct .unpack (endian + 'I' , binary [5 :9 ])[0 ]
73+ if wkb_type != 1 : # WKB type 1 means POINT
74+ raise ValueError ("Not a WKB POINT type" )
75+ # Read X and Y coordinates
76+ x = struct .unpack (endian + 'd' , binary [9 :17 ])[0 ]
77+ y = struct .unpack (endian + 'd' , binary [17 :25 ])[0 ]
78+ else :
79+ raise ValueError ("Invalid binary length for WKB POINT" )
80+ return (x , y )
81+
82+
3083def strip_sql_name (name ):
3184 name = name .strip ()
3285 if name .startswith ('`' ):
@@ -64,9 +117,11 @@ def __init__(self, db_replicator: 'DbReplicator' = None):
64117 self .db_replicator = db_replicator
65118
66119 def convert_type (self , mysql_type , parameters ):
67-
68120 is_unsigned = 'unsigned' in parameters .lower ()
69121
122+ if mysql_type == 'point' :
123+ return 'Tuple(x Float32, y Float32)'
124+
70125 if mysql_type == 'int' :
71126 if is_unsigned :
72127 return 'UInt32'
@@ -89,6 +144,8 @@ def convert_type(self, mysql_type, parameters):
89144 return 'Date32'
90145 if mysql_type == 'tinyint(1)' :
91146 return 'Bool'
147+ if mysql_type == 'bit(1)' :
148+ return 'Bool'
92149 if mysql_type == 'bool' :
93150 return 'Bool'
94151 if 'smallint' in mysql_type :
@@ -123,27 +180,31 @@ def convert_type(self, mysql_type, parameters):
123180 return 'Float32'
124181 if 'double' in mysql_type :
125182 return 'Float64'
126- if 'integer' in mysql_type or 'int(' in mysql_type :
127- if is_unsigned :
128- return 'UInt32'
129- return 'Int32'
130183 if 'bigint' in mysql_type :
131184 if is_unsigned :
132185 return 'UInt64'
133186 return 'Int64'
187+ if 'integer' in mysql_type or 'int(' in mysql_type :
188+ if is_unsigned :
189+ return 'UInt32'
190+ return 'Int32'
134191 if 'real' in mysql_type :
135192 return 'Float64'
136193 if mysql_type .startswith ('time' ):
137194 return 'String'
138195 if 'varbinary' in mysql_type :
139196 return 'String'
197+ if 'binary' in mysql_type :
198+ return 'String'
140199 raise Exception (f'unknown mysql type "{ mysql_type } "' )
141200
142201 def convert_field_type (self , mysql_type , mysql_parameters ):
143202 mysql_type = mysql_type .lower ()
144203 mysql_parameters = mysql_parameters .lower ()
145204 not_null = 'not null' in mysql_parameters
146205 clickhouse_type = self .convert_type (mysql_type , mysql_parameters )
206+ if 'Tuple' in clickhouse_type :
207+ not_null = True
147208 if not not_null :
148209 clickhouse_type = f'Nullable({ clickhouse_type } )'
149210 return clickhouse_type
@@ -195,6 +256,9 @@ def convert_record(self, mysql_record, mysql_field_types, clickhouse_field_types
195256 if 'UInt64' in clickhouse_field_type and clickhouse_field_value < 0 :
196257 clickhouse_field_value = 18446744073709551616 + clickhouse_field_value
197258
259+ if 'point' in mysql_field_type :
260+ clickhouse_field_value = parse_mysql_point (clickhouse_field_value )
261+
198262 clickhouse_record .append (clickhouse_field_value )
199263 return tuple (clickhouse_record )
200264
0 commit comments