mirror of
https://github.com/alibaba/canal.git
synced 2026-11-04 00:30:15 +08:00
fixed issue #1070, support mysql8.0.13
This commit is contained in:
@@ -275,6 +275,7 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
|
||||
needDmlParse = true;
|
||||
break;
|
||||
case LogEvent.UPDATE_ROWS_EVENT_V1:
|
||||
case LogEvent.PARTIAL_UPDATE_ROWS_EVENT:
|
||||
case LogEvent.UPDATE_ROWS_EVENT:
|
||||
tableMeta = logEventConvert.parseRowsEventForTableMeta((UpdateRowsLogEvent) logEvent);
|
||||
needDmlParse = true;
|
||||
|
||||
@@ -120,6 +120,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
|
||||
case LogEvent.WRITE_ROWS_EVENT:
|
||||
return parseRowsEvent((WriteRowsLogEvent) logEvent);
|
||||
case LogEvent.UPDATE_ROWS_EVENT_V1:
|
||||
case LogEvent.PARTIAL_UPDATE_ROWS_EVENT:
|
||||
case LogEvent.UPDATE_ROWS_EVENT:
|
||||
return parseRowsEvent((UpdateRowsLogEvent) logEvent);
|
||||
case LogEvent.DELETE_ROWS_EVENT_V1:
|
||||
@@ -261,7 +262,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
|
||||
}
|
||||
|
||||
boolean isDml = (type == EventType.INSERT || type == EventType.UPDATE || type == EventType.DELETE);
|
||||
|
||||
|
||||
if (!isSeek && !isDml) {
|
||||
// 使用新的表结构元数据管理方式
|
||||
EntryPosition position = createPosition(event.getHeader());
|
||||
@@ -504,7 +505,8 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
|
||||
int type = event.getHeader().getType();
|
||||
if (LogEvent.WRITE_ROWS_EVENT_V1 == type || LogEvent.WRITE_ROWS_EVENT == type) {
|
||||
eventType = EventType.INSERT;
|
||||
} else if (LogEvent.UPDATE_ROWS_EVENT_V1 == type || LogEvent.UPDATE_ROWS_EVENT == type) {
|
||||
} else if (LogEvent.UPDATE_ROWS_EVENT_V1 == type || LogEvent.UPDATE_ROWS_EVENT == type
|
||||
|| LogEvent.PARTIAL_UPDATE_ROWS_EVENT == type) {
|
||||
eventType = EventType.UPDATE;
|
||||
} else if (LogEvent.DELETE_ROWS_EVENT_V1 == type || LogEvent.DELETE_ROWS_EVENT == type) {
|
||||
eventType = EventType.DELETE;
|
||||
@@ -523,7 +525,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
|
||||
|
||||
boolean tableError = false;
|
||||
int rowsCount = 0;
|
||||
while (buffer.nextOneRow(columns)) {
|
||||
while (buffer.nextOneRow(columns, false)) {
|
||||
// 处理row记录
|
||||
RowData.Builder rowDataBuilder = RowData.newBuilder();
|
||||
if (EventType.INSERT == eventType) {
|
||||
@@ -535,7 +537,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
|
||||
} else {
|
||||
// update需要处理before/after
|
||||
tableError |= parseOneRow(rowDataBuilder, event, buffer, columns, false, tableMeta);
|
||||
if (!buffer.nextOneRow(changeColumns)) {
|
||||
if (!buffer.nextOneRow(changeColumns, true)) {
|
||||
rowChangeBuider.addRowDatas(rowDataBuilder.build());
|
||||
break;
|
||||
}
|
||||
@@ -577,7 +579,8 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
|
||||
boolean isAfter, TableMeta tableMeta) throws UnsupportedEncodingException {
|
||||
int columnCnt = event.getTable().getColumnCnt();
|
||||
ColumnInfo[] columnInfo = event.getTable().getColumnInfo();
|
||||
|
||||
// mysql8.0针对set @@global.binlog_row_metadata='FULL' 可以记录部分的metadata信息
|
||||
boolean existOptionalMetaData = event.getTable().isExistOptionalMetaData();
|
||||
boolean tableError = false;
|
||||
// check table fileds count,只能处理加字段
|
||||
boolean existRDSNoPrimaryKey = false;
|
||||
@@ -631,22 +634,43 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existRDSNoPrimaryKey && i == columnCnt - 1 && info.type == LogEvent.MYSQL_TYPE_LONGLONG) {
|
||||
// 不解析最后一列
|
||||
buffer.nextValue(info.type, info.meta, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
Column.Builder columnBuilder = Column.newBuilder();
|
||||
|
||||
FieldMeta fieldMeta = null;
|
||||
if (tableMeta != null && !tableError) {
|
||||
// 处理file meta
|
||||
fieldMeta = tableMeta.getFields().get(i);
|
||||
}
|
||||
|
||||
if (existRDSNoPrimaryKey && i == columnCnt - 1 && info.type == LogEvent.MYSQL_TYPE_LONGLONG) {
|
||||
// 不解析最后一列
|
||||
buffer.nextValue(fieldMeta.getColumnName(), i, info.type, info.meta, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fieldMeta != null && existOptionalMetaData) {
|
||||
// check column info
|
||||
boolean check = StringUtils.equalsIgnoreCase(fieldMeta.getColumnName(), info.name);
|
||||
check &= (fieldMeta.isUnsigned() == info.unsigned);
|
||||
check &= (fieldMeta.isNullable() == info.nullable);
|
||||
|
||||
if (!check) {
|
||||
throw new CanalParseException("MySQL8.0 unmatch column metadata & pls submit issue , db : "
|
||||
+ fieldMeta.toString() + " , binlog : " + info.toString()
|
||||
+ " , on : " + event.getHeader().getLogFileName() + ":"
|
||||
+ event.getHeader().getLogPos());
|
||||
}
|
||||
}
|
||||
|
||||
Column.Builder columnBuilder = Column.newBuilder();
|
||||
if (fieldMeta != null) {
|
||||
columnBuilder.setName(fieldMeta.getColumnName());
|
||||
columnBuilder.setIsKey(fieldMeta.isKey());
|
||||
// 增加mysql type类型,issue 73
|
||||
columnBuilder.setMysqlType(fieldMeta.getColumnType());
|
||||
} else if (existOptionalMetaData) {
|
||||
columnBuilder.setName(info.name);
|
||||
columnBuilder.setIsKey(info.pk);
|
||||
// mysql8.0里没有mysql type类型
|
||||
// columnBuilder.setMysqlType(fieldMeta.getColumnType());
|
||||
}
|
||||
columnBuilder.setIndex(i);
|
||||
columnBuilder.setIsNull(false);
|
||||
@@ -664,12 +688,8 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
|
||||
isSingleBit = true;
|
||||
}
|
||||
}
|
||||
buffer.nextValue(info.type, info.meta, isBinary);
|
||||
if (existRDSNoPrimaryKey && i == columnCnt - 1 && info.type == LogEvent.MYSQL_TYPE_LONGLONG) {
|
||||
// 不解析最后一列
|
||||
continue;
|
||||
}
|
||||
|
||||
buffer.nextValue(fieldMeta.getColumnName(), i, info.type, info.meta, isBinary);
|
||||
int javaType = buffer.getJavaType();
|
||||
if (buffer.isNull()) {
|
||||
columnBuilder.setIsNull(true);
|
||||
@@ -683,7 +703,8 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
|
||||
case Types.BIGINT:
|
||||
// 处理unsigned类型
|
||||
Number number = (Number) value;
|
||||
if (fieldMeta != null && fieldMeta.isUnsigned() && number.longValue() < 0) {
|
||||
boolean isUnsigned = (fieldMeta != null ? fieldMeta.isUnsigned() : (existOptionalMetaData ? info.unsigned : false));
|
||||
if (isUnsigned && number.longValue() < 0) {
|
||||
switch (buffer.getLength()) {
|
||||
case 1: /* MYSQL_TYPE_TINY */
|
||||
columnBuilder.setValue(String.valueOf(Integer.valueOf(TINYINT_MAX_VALUE
|
||||
@@ -772,7 +793,6 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
|
||||
default:
|
||||
columnBuilder.setValue(value.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
columnBuilder.setSqlType(javaType);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.alibaba.otter.canal.parse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@@ -24,12 +28,25 @@ import com.alibaba.otter.canal.parse.inbound.mysql.dbsync.DirectLogFetcher;
|
||||
import com.taobao.tddl.dbsync.binlog.LogContext;
|
||||
import com.taobao.tddl.dbsync.binlog.LogDecoder;
|
||||
import com.taobao.tddl.dbsync.binlog.LogEvent;
|
||||
import com.taobao.tddl.dbsync.binlog.event.DeleteRowsLogEvent;
|
||||
import com.taobao.tddl.dbsync.binlog.event.FormatDescriptionLogEvent;
|
||||
import com.taobao.tddl.dbsync.binlog.event.QueryLogEvent;
|
||||
import com.taobao.tddl.dbsync.binlog.event.RotateLogEvent;
|
||||
import com.taobao.tddl.dbsync.binlog.event.RowsLogBuffer;
|
||||
import com.taobao.tddl.dbsync.binlog.event.RowsLogEvent;
|
||||
import com.taobao.tddl.dbsync.binlog.event.RowsQueryLogEvent;
|
||||
import com.taobao.tddl.dbsync.binlog.event.TableMapLogEvent;
|
||||
import com.taobao.tddl.dbsync.binlog.event.TableMapLogEvent.ColumnInfo;
|
||||
import com.taobao.tddl.dbsync.binlog.event.UpdateRowsLogEvent;
|
||||
import com.taobao.tddl.dbsync.binlog.event.WriteRowsLogEvent;
|
||||
import com.taobao.tddl.dbsync.binlog.event.XidLogEvent;
|
||||
import com.taobao.tddl.dbsync.binlog.event.mariadb.AnnotateRowsEvent;
|
||||
|
||||
public class DirectLogFetcherTest {
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
protected String binlogFileName = "mysql-bin.000001";
|
||||
protected Charset charset = Charset.forName("utf-8");
|
||||
private boolean isMariaDB;
|
||||
private int binlogChecksum;
|
||||
|
||||
@@ -37,7 +54,7 @@ public class DirectLogFetcherTest {
|
||||
public void testSimple() {
|
||||
DirectLogFetcher fetcher = new DirectLogFetcher();
|
||||
try {
|
||||
MysqlConnector connector = new MysqlConnector(new InetSocketAddress("127.0.0.1", 3306), "xxxx", "xxxx");
|
||||
MysqlConnector connector = new MysqlConnector(new InetSocketAddress("127.0.0.1", 3306), "root", "hello");
|
||||
connector.connect();
|
||||
updateSettings(connector);
|
||||
loadBinlogChecksum(connector);
|
||||
@@ -67,21 +84,22 @@ public class DirectLogFetcherTest {
|
||||
break;
|
||||
case LogEvent.WRITE_ROWS_EVENT_V1:
|
||||
case LogEvent.WRITE_ROWS_EVENT:
|
||||
// parseRowsEvent((WriteRowsLogEvent) event);
|
||||
parseRowsEvent((WriteRowsLogEvent) event);
|
||||
break;
|
||||
case LogEvent.UPDATE_ROWS_EVENT_V1:
|
||||
case LogEvent.PARTIAL_UPDATE_ROWS_EVENT:
|
||||
case LogEvent.UPDATE_ROWS_EVENT:
|
||||
// parseRowsEvent((UpdateRowsLogEvent) event);
|
||||
parseRowsEvent((UpdateRowsLogEvent) event);
|
||||
break;
|
||||
case LogEvent.DELETE_ROWS_EVENT_V1:
|
||||
case LogEvent.DELETE_ROWS_EVENT:
|
||||
// parseRowsEvent((DeleteRowsLogEvent) event);
|
||||
parseRowsEvent((DeleteRowsLogEvent) event);
|
||||
break;
|
||||
case LogEvent.QUERY_EVENT:
|
||||
// parseQueryEvent((QueryLogEvent) event);
|
||||
parseQueryEvent((QueryLogEvent) event);
|
||||
break;
|
||||
case LogEvent.ROWS_QUERY_LOG_EVENT:
|
||||
// parseRowsQueryEvent((RowsQueryLogEvent) event);
|
||||
parseRowsQueryEvent((RowsQueryLogEvent) event);
|
||||
break;
|
||||
case LogEvent.ANNOTATE_ROWS_EVENT:
|
||||
break;
|
||||
@@ -241,4 +259,98 @@ public class DirectLogFetcherTest {
|
||||
exector.update(cmd);
|
||||
}
|
||||
|
||||
protected void parseQueryEvent(QueryLogEvent event) {
|
||||
System.out.println(String.format("================> binlog[%s:%s] , name[%s]",
|
||||
binlogFileName,
|
||||
event.getHeader().getLogPos() - event.getHeader().getEventLen(),
|
||||
event.getCatalog()));
|
||||
System.out.println("sql : " + event.getQuery());
|
||||
}
|
||||
|
||||
protected void parseRowsQueryEvent(RowsQueryLogEvent event) throws Exception {
|
||||
System.out.println(String.format("================> binlog[%s:%s]", binlogFileName, event.getHeader()
|
||||
.getLogPos() - event.getHeader().getEventLen()));
|
||||
System.out.println("sql : " + new String(event.getRowsQuery().getBytes("ISO-8859-1"), charset.name()));
|
||||
}
|
||||
|
||||
protected void parseAnnotateRowsEvent(AnnotateRowsEvent event) throws Exception {
|
||||
System.out.println(String.format("================> binlog[%s:%s]", binlogFileName, event.getHeader()
|
||||
.getLogPos() - event.getHeader().getEventLen()));
|
||||
System.out.println("sql : " + new String(event.getRowsQuery().getBytes("ISO-8859-1"), charset.name()));
|
||||
}
|
||||
|
||||
protected void parseXidEvent(XidLogEvent event) throws Exception {
|
||||
System.out.println(String.format("================> binlog[%s:%s]", binlogFileName, event.getHeader()
|
||||
.getLogPos() - event.getHeader().getEventLen()));
|
||||
System.out.println("xid : " + event.getXid());
|
||||
}
|
||||
|
||||
protected void parseRowsEvent(RowsLogEvent event) {
|
||||
try {
|
||||
System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s]",
|
||||
binlogFileName,
|
||||
event.getHeader().getLogPos() - event.getHeader().getEventLen(),
|
||||
event.getTable().getDbName(),
|
||||
event.getTable().getTableName()));
|
||||
RowsLogBuffer buffer = event.getRowsBuf(charset.name());
|
||||
BitSet columns = event.getColumns();
|
||||
BitSet changeColumns = event.getChangeColumns();
|
||||
while (buffer.nextOneRow(columns)) {
|
||||
// 处理row记录
|
||||
int type = event.getHeader().getType();
|
||||
if (LogEvent.WRITE_ROWS_EVENT_V1 == type || LogEvent.WRITE_ROWS_EVENT == type) {
|
||||
// insert的记录放在before字段中
|
||||
parseOneRow(event, buffer, columns, true);
|
||||
} else if (LogEvent.DELETE_ROWS_EVENT_V1 == type || LogEvent.DELETE_ROWS_EVENT == type) {
|
||||
// delete的记录放在before字段中
|
||||
parseOneRow(event, buffer, columns, false);
|
||||
} else {
|
||||
// update需要处理before/after
|
||||
System.out.println("-------> before");
|
||||
parseOneRow(event, buffer, columns, false);
|
||||
if (!buffer.nextOneRow(changeColumns, true)) {
|
||||
break;
|
||||
}
|
||||
System.out.println("-------> after");
|
||||
parseOneRow(event, buffer, changeColumns, true);
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("parse row data failed.", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseOneRow(RowsLogEvent event, RowsLogBuffer buffer, BitSet cols, boolean isAfter)
|
||||
throws UnsupportedEncodingException {
|
||||
TableMapLogEvent map = event.getTable();
|
||||
if (map == null) {
|
||||
throw new RuntimeException("not found TableMap with tid=" + event.getTableId());
|
||||
}
|
||||
|
||||
final int columnCnt = map.getColumnCnt();
|
||||
final ColumnInfo[] columnInfo = map.getColumnInfo();
|
||||
|
||||
for (int i = 0; i < columnCnt; i++) {
|
||||
if (!cols.get(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ColumnInfo info = columnInfo[i];
|
||||
buffer.nextValue(null, i, info.type, info.meta);
|
||||
|
||||
if (buffer.isNull()) {
|
||||
//
|
||||
} else {
|
||||
final Serializable value = buffer.getValue();
|
||||
if (value instanceof byte[]) {
|
||||
System.out.println(new String((byte[]) value));
|
||||
} else {
|
||||
System.out.println(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ public class MysqlBinlogParsePerformanceTest {
|
||||
parseRowsEvent((WriteRowsLogEvent) event, sum);
|
||||
break;
|
||||
case LogEvent.UPDATE_ROWS_EVENT_V1:
|
||||
case LogEvent.PARTIAL_UPDATE_ROWS_EVENT:
|
||||
case LogEvent.UPDATE_ROWS_EVENT:
|
||||
parseRowsEvent((UpdateRowsLogEvent) event, sum);
|
||||
break;
|
||||
@@ -154,7 +155,7 @@ public class MysqlBinlogParsePerformanceTest {
|
||||
parseOneRow(event, buffer, columns, false);
|
||||
} else {
|
||||
parseOneRow(event, buffer, columns, false);
|
||||
if (!buffer.nextOneRow(changeColumns)) {
|
||||
if (!buffer.nextOneRow(changeColumns, true)) {
|
||||
break;
|
||||
}
|
||||
parseOneRow(event, buffer, changeColumns, true);
|
||||
@@ -182,7 +183,7 @@ public class MysqlBinlogParsePerformanceTest {
|
||||
}
|
||||
|
||||
ColumnInfo info = columnInfo[i];
|
||||
buffer.nextValue(info.type, info.meta);
|
||||
buffer.nextValue(null, i, info.type, info.meta);
|
||||
if (buffer.isNull()) {
|
||||
} else {
|
||||
buffer.getValue();
|
||||
|
||||
Reference in New Issue
Block a user