11using System ;
2+ using System . Buffers . Binary ;
3+ using System . Collections . Generic ;
4+ using System . Data ;
25using System . IO ;
3- using System . Threading . Tasks ;
6+ using System . Linq ;
47using System . Reflection ;
5- using System . Buffers . Binary ;
8+ using System . Threading . Tasks ;
69using Microsoft . Extensions . Logging ;
710using MySql . Data . MySqlClient ;
8- using SuperSocket . Connection ;
9- using SuperSocket . Client ;
1011using SciSharp . MySQL . Replication . Events ;
12+ using SuperSocket . Client ;
13+ using SuperSocket . Connection ;
1114
1215namespace SciSharp . MySQL . Replication
1316{
@@ -40,6 +43,8 @@ public class ReplicationClient : EasyClient<LogEvent>, IReplicationClient
4043 set { base . Logger = value ; }
4144 }
4245
46+ private readonly Dictionary < string , TableSchema > _tableSchemaMap ;
47+
4348 static ReplicationClient ( )
4449 {
4550 LogEventPackageDecoder . RegisterEmptyPayloadEventTypes (
@@ -68,11 +73,17 @@ static ReplicationClient()
6873 /// Initializes a new instance of the <see cref="ReplicationClient"/> class.
6974 /// </summary>
7075 public ReplicationClient ( )
71- : base ( new LogEventPipelineFilter ( ) )
76+ : this ( new LogEventPipelineFilter ( ) )
7277 {
7378
7479 }
7580
81+ private ReplicationClient ( LogEventPipelineFilter logEventPipelineFilter )
82+ : base ( logEventPipelineFilter )
83+ {
84+ _tableSchemaMap = ( logEventPipelineFilter . Context as ReplicationState ) . TableSchemaMap ;
85+ }
86+
7687 /// <summary>
7788 /// Gets the underlying stream from a MySQL connection.
7889 /// </summary>
@@ -104,7 +115,7 @@ public async Task<LoginResult> ConnectAsync(string server, string username, stri
104115
105116 try
106117 {
107- await mysqlConn . OpenAsync ( ) ;
118+ await mysqlConn . OpenAsync ( ) . ConfigureAwait ( false ) ;
108119 }
109120 catch ( Exception e )
110121 {
@@ -117,16 +128,18 @@ public async Task<LoginResult> ConnectAsync(string server, string username, stri
117128
118129 try
119130 {
120- var binlogInfo = await GetBinlogFileNameAndPosition ( mysqlConn ) ;
131+ await LoadDatabaseSchemaAsync ( mysqlConn ) . ConfigureAwait ( false ) ;
132+
133+ var binlogInfo = await GetBinlogFileNameAndPosition ( mysqlConn ) . ConfigureAwait ( false ) ;
121134
122- var binlogChecksum = await GetBinlogChecksum ( mysqlConn ) ;
123- await ConfirmChecksum ( mysqlConn ) ;
135+ var binlogChecksum = await GetBinlogChecksum ( mysqlConn ) . ConfigureAwait ( false ) ;
136+ await ConfirmChecksum ( mysqlConn ) . ConfigureAwait ( false ) ;
124137 LogEvent . ChecksumType = binlogChecksum ;
125138
126139 _stream = GetStreamFromMySQLConnection ( mysqlConn ) ;
127140 _serverId = serverId ;
128141
129- await StartDumpBinlog ( _stream , serverId , binlogInfo . Item1 , binlogInfo . Item2 ) ;
142+ await StartDumpBinlog ( _stream , serverId , binlogInfo . Item1 , binlogInfo . Item2 ) . ConfigureAwait ( false ) ;
130143
131144 _connection = mysqlConn ;
132145
@@ -143,7 +156,7 @@ public async Task<LoginResult> ConnectAsync(string server, string username, stri
143156 }
144157 catch ( Exception e )
145158 {
146- await mysqlConn . CloseAsync ( ) ;
159+ await mysqlConn . CloseAsync ( ) . ConfigureAwait ( false ) ;
147160
148161 return new LoginResult
149162 {
@@ -153,6 +166,50 @@ public async Task<LoginResult> ConnectAsync(string server, string username, stri
153166 }
154167 }
155168
169+ private async Task LoadDatabaseSchemaAsync ( MySqlConnection mysqlConn )
170+ {
171+ var tableSchemaTable = await mysqlConn . GetSchemaAsync ( "Columns" ) . ConfigureAwait ( false ) ;
172+
173+ var systemDatabases = new HashSet < string > (
174+ new [ ] { "mysql" , "information_schema" , "performance_schema" , "sys" } ,
175+ StringComparer . OrdinalIgnoreCase ) ;
176+
177+ var userDatabaseColumns = tableSchemaTable . Rows . OfType < DataRow > ( )
178+ . Where ( row => ! systemDatabases . Contains ( row . ItemArray [ 1 ] . ToString ( ) ) )
179+ . ToArray ( ) ;
180+
181+ userDatabaseColumns . Select ( row =>
182+ {
183+ var columnSizeCell = row [ "CHARACTER_MAXIMUM_LENGTH" ] ;
184+
185+ return new {
186+ TableName = row [ "TABLE_NAME" ] . ToString ( ) ,
187+ DatabaseName = row [ "TABLE_SCHEMA" ] . ToString ( ) ,
188+ ColumnName = row [ "COLUMN_NAME" ] . ToString ( ) ,
189+ ColumnType = row [ "DATA_TYPE" ] . ToString ( ) ,
190+ ColumnSize = columnSizeCell == DBNull . Value ? 0 : Convert . ToUInt64 ( columnSizeCell ) ,
191+ } ;
192+ } )
193+ . GroupBy ( row => new { row . TableName , row . DatabaseName } )
194+ . ToList ( )
195+ . ForEach ( group =>
196+ {
197+ var tableSchema = new TableSchema
198+ {
199+ TableName = group . Key . TableName ,
200+ DatabaseName = group . Key . DatabaseName ,
201+ Columns = group . Select ( row => new ColumnSchema
202+ {
203+ Name = row . ColumnName ,
204+ DataType = row . ColumnType ,
205+ ColumnSize = row . ColumnSize
206+ } ) . ToList ( )
207+ } ;
208+
209+ _tableSchemaMap [ $ "{ group . Key . DatabaseName } .{ group . Key . TableName } "] = tableSchema ;
210+ } ) ;
211+ }
212+
156213 /// <summary>
157214 /// Retrieves the binary log file name and position from the MySQL server.
158215 /// https://dev.mysql.com/doc/refman/5.6/en/replication-howto-masterstatus.html
@@ -164,15 +221,15 @@ private async Task<Tuple<string, int>> GetBinlogFileNameAndPosition(MySqlConnect
164221 var cmd = mysqlConn . CreateCommand ( ) ;
165222 cmd . CommandText = "SHOW MASTER STATUS;" ;
166223
167- using ( var reader = await cmd . ExecuteReaderAsync ( ) )
224+ using ( var reader = await cmd . ExecuteReaderAsync ( ) . ConfigureAwait ( false ) )
168225 {
169226 if ( ! await reader . ReadAsync ( ) )
170227 throw new Exception ( "No binlog information has been returned." ) ;
171228
172229 var fileName = reader . GetString ( 0 ) ;
173230 var position = reader . GetInt32 ( 1 ) ;
174231
175- await reader . CloseAsync ( ) ;
232+ await reader . CloseAsync ( ) . ConfigureAwait ( false ) ;
176233
177234 return new Tuple < string , int > ( fileName , position ) ;
178235 }
@@ -190,11 +247,11 @@ private async Task<ChecksumType> GetBinlogChecksum(MySqlConnection mysqlConn)
190247
191248 using ( var reader = await cmd . ExecuteReaderAsync ( ) )
192249 {
193- if ( ! await reader . ReadAsync ( ) )
250+ if ( ! await reader . ReadAsync ( ) . ConfigureAwait ( false ) )
194251 return ChecksumType . NONE ;
195252
196253 var checksumTypeName = reader . GetString ( 1 ) . ToUpper ( ) ;
197- await reader . CloseAsync ( ) ;
254+ await reader . CloseAsync ( ) . ConfigureAwait ( false ) ;
198255
199256 return ( ChecksumType ) Enum . Parse ( typeof ( ChecksumType ) , checksumTypeName ) ;
200257 }
@@ -209,7 +266,7 @@ private async ValueTask ConfirmChecksum(MySqlConnection mysqlConn)
209266 {
210267 var cmd = mysqlConn . CreateCommand ( ) ;
211268 cmd . CommandText = "set @`master_binlog_checksum` = @@binlog_checksum;" ;
212- await cmd . ExecuteNonQueryAsync ( ) ;
269+ await cmd . ExecuteNonQueryAsync ( ) . ConfigureAwait ( false ) ;
213270 }
214271
215272 /// <summary>
@@ -269,8 +326,8 @@ private Memory<byte> GetDumpBinlogCommand(int serverId, string fileName, int pos
269326 private async ValueTask StartDumpBinlog ( Stream stream , int serverId , string fileName , int position )
270327 {
271328 var data = GetDumpBinlogCommand ( serverId , fileName , position ) ;
272- await stream . WriteAsync ( data ) ;
273- await stream . FlushAsync ( ) ;
329+ await stream . WriteAsync ( data ) . ConfigureAwait ( false ) ;
330+ await stream . FlushAsync ( ) . ConfigureAwait ( false ) ;
274331 }
275332
276333 /// <summary>
@@ -307,10 +364,10 @@ public override async ValueTask CloseAsync()
307364 if ( connection != null )
308365 {
309366 _connection = null ;
310- await connection . CloseAsync ( ) ;
367+ await connection . CloseAsync ( ) . ConfigureAwait ( false ) ;
311368 }
312369
313- await base . CloseAsync ( ) ;
370+ await base . CloseAsync ( ) . ConfigureAwait ( false ) ;
314371 }
315372 }
316373}
0 commit comments