diff --git a/client/src/main/java/com/alibaba/otter/canal/client/CanalMessageDeserializer.java b/client/src/main/java/com/alibaba/otter/canal/client/CanalMessageDeserializer.java index b2206890..9d24a23b 100644 --- a/client/src/main/java/com/alibaba/otter/canal/client/CanalMessageDeserializer.java +++ b/client/src/main/java/com/alibaba/otter/canal/client/CanalMessageDeserializer.java @@ -32,10 +32,12 @@ public class CanalMessageDeserializer { if (lazyParseEntry) { // byteString result.setRawEntries(messages.getMessagesList()); + result.setRaw(true); } else { for (ByteString byteString : messages.getMessagesList()) { result.addEntry(CanalEntry.Entry.parseFrom(byteString)); } + result.setRaw(false); } return result; } diff --git a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/JsonConversion.java b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/JsonConversion.java index 020591f7..e61b6645 100644 --- a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/JsonConversion.java +++ b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/JsonConversion.java @@ -1,5 +1,9 @@ package com.taobao.tddl.dbsync.binlog; +import static com.taobao.tddl.dbsync.binlog.event.RowsLogBuffer.appendNumber2; +import static com.taobao.tddl.dbsync.binlog.event.RowsLogBuffer.appendNumber4; +import static com.taobao.tddl.dbsync.binlog.event.RowsLogBuffer.usecondsToStr; + /** * 处理下MySQL json二进制转化为可读的字符串 * @@ -330,12 +334,30 @@ public class JsonConversion { long ultime = Math.abs(packed_value); long intpart = ultime >> 24; int frac = (int) (ultime % (1L << 24)); - text = String.format("%s%02d:%02d:%02d", - packed_value >= 0 ? "" : "-", - (int) ((intpart >> 12) % (1 << 10)), - (int) ((intpart >> 6) % (1 << 6)), - (int) (intpart % (1 << 6))); - text = text + "." + usecondsToStr(frac, 6); + // text = String.format("%s%02d:%02d:%02d", + // packed_value >= 0 ? "" : "-", + // (int) ((intpart >> 12) % (1 << 10)), + // (int) ((intpart >> 6) % (1 << 6)), + // (int) (intpart % (1 << 6))); + // text = text + "." + usecondsToStr(frac, 6); + StringBuilder builder = new StringBuilder(17); + if (packed_value < 0) { + builder.append('-'); + } + + int d = (int) ((intpart >> 12) % (1 << 10)); + if (d > 100) { + builder.append(String.valueOf(d)); + } else { + appendNumber2(builder, d); + } + builder.append(':'); + appendNumber2(builder, (int) ((intpart >> 6) % (1 << 6))); + builder.append(':'); + appendNumber2(builder, (int) (intpart % (1 << 6))); + + builder.append('.').append(usecondsToStr(frac, 6)); + text = builder.toString(); } buf.append('"').append(text).append('"'); } else if (m_field_type == LogEvent.MYSQL_TYPE_DATE || m_field_type == LogEvent.MYSQL_TYPE_DATETIME @@ -351,14 +373,28 @@ public class JsonConversion { long ymd = intpart >> 17; long ym = ymd >> 5; long hms = intpart % (1 << 17); - text = String.format("%04d-%02d-%02d %02d:%02d:%02d", - (int) (ym / 13), - (int) (ym % 13), - (int) (ymd % (1 << 5)), - (int) (hms >> 12), - (int) ((hms >> 6) % (1 << 6)), - (int) (hms % (1 << 6))); - text = text + "." + usecondsToStr(frac, 6); + // text = + // String.format("%04d-%02d-%02d %02d:%02d:%02d", + // (int) (ym / 13), + // (int) (ym % 13), + // (int) (ymd % (1 << 5)), + // (int) (hms >> 12), + // (int) ((hms >> 6) % (1 << 6)), + // (int) (hms % (1 << 6))); + StringBuilder builder = new StringBuilder(26); + appendNumber4(builder, (int) (ym / 13)); + builder.append('-'); + appendNumber2(builder, (int) (ym % 13)); + builder.append('-'); + appendNumber2(builder, (int) (ymd % (1 << 5))); + builder.append(' '); + appendNumber2(builder, (int) (hms >> 12)); + builder.append(':'); + appendNumber2(builder, (int) ((hms >> 6) % (1 << 6))); + builder.append(':'); + appendNumber2(builder, (int) (hms % (1 << 6))); + builder.append('.').append(usecondsToStr(frac, 6)); + text = builder.toString(); } buf.append('"').append(text).append('"'); } else { @@ -397,22 +433,4 @@ public class JsonConversion { OBJECT, ARRAY, STRING, INT, UINT, DOUBLE, LITERAL_NULL, LITERAL_TRUE, LITERAL_FALSE, OPAQUE, ERROR } - private static String usecondsToStr(int frac, int meta) { - String sec = String.valueOf(frac); - if (meta > 6) { - throw new IllegalArgumentException("unknow useconds meta : " + meta); - } - - if (sec.length() < 6) { - StringBuilder result = new StringBuilder(6); - int len = 6 - sec.length(); - for (; len > 0; len--) { - result.append('0'); - } - result.append(sec); - sec = result.toString(); - } - - return sec.substring(0, meta); - } } diff --git a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/JsonDiffConversion.java b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/JsonDiffConversion.java new file mode 100644 index 00000000..223b5d9c --- /dev/null +++ b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/JsonDiffConversion.java @@ -0,0 +1,154 @@ +package com.taobao.tddl.dbsync.binlog; + +import java.util.ArrayList; +import java.util.List; + +import com.taobao.tddl.dbsync.binlog.JsonConversion.Json_Value; +import com.taobao.tddl.dbsync.binlog.JsonConversion.Json_enum_type; + +/** + * 处理mysql8.0 parital json diff解析 + * + * @author agapple 2018年11月4日 下午3:53:46 + * @since 1.1.2 + */ +public class JsonDiffConversion { + + /** + * The JSON value in the given path is replaced with a new value. It has the + * same effect as `JSON_REPLACE(col, path, value)`. + */ + public static final int DIFF_OPERATION_REPLACE = 0; + /** + * Add a new element at the given path. If the path specifies an array + * element, it has the same effect as `JSON_ARRAY_INSERT(col, path, value)`. + * If the path specifies an object member, it has the same effect as + * `JSON_INSERT(col, path, value)`. + */ + public static final int DIFF_OPERATION_INSERT = 1; + /** + * The JSON value at the given path is removed from an array or object. It + * has the same effect as `JSON_REMOVE(col, path)`. + */ + public static final int DIFF_OPERATION_REMOVE = 2; + + public static final int JSON_DIFF_OPERATION_COUNT = 3; + + public static StringBuilder print_json_diff(LogBuffer buffer, long len, String columnName, int columnIndex, + String charsetName) { + int position = buffer.position(); + List operation_names = new ArrayList(); + while (buffer.hasRemaining()) { + int operation_int = buffer.getUint8(); + if (operation_int >= JSON_DIFF_OPERATION_COUNT) { + throw new IllegalArgumentException("reading operation type (invalid operation code)"); + } + + // skip path + long path_length = buffer.getPackedLong(); + if (path_length > len) { + throw new IllegalArgumentException("skipping path"); + } + + // compute operation name + byte[] lastP = buffer.getData(buffer.position() + (int) path_length - 1, 1); + String operation_name = json_diff_operation_name(operation_int, lastP[0]); + operation_names.add(operation_name); + + buffer.forward((int) path_length); + // skip value + if (operation_int != DIFF_OPERATION_REMOVE) { + long value_length = buffer.getPackedLong(); + if (value_length > len) { + throw new IllegalArgumentException("skipping path"); + } + + buffer.forward((int) value_length); + } + } + + // Print function names in reverse order. + StringBuilder builder = new StringBuilder(); + for (int i = operation_names.size() - 1; i >= 0; i--) { + if (i == 0 || operation_names.get(i - 1) != operation_names.get(i)) { + builder.append(operation_names.get(i)).append("("); + } + } + + // Print column id + if (columnName != null) { + builder.append(columnName); + } else { + builder.append("@").append(columnIndex); + } + + // In case this vector is empty (a no-op), make an early return + // after printing only the column name + if (operation_names.size() == 0) { + return builder; + } + + // Print comma between column name and next function argument + builder.append(", "); + // Print paths and values. + buffer.position(position); + int diff_i = 0; + while (buffer.hasRemaining()) { + // Read operation + int operation_int = buffer.getUint8(); + + // Read path length + long path_length = buffer.getPackedLong(); + // Print path + builder.append('\'').append(buffer.getFixString((int) path_length)).append('\''); + + if (operation_int != DIFF_OPERATION_REMOVE) { + // Print comma between path and value + builder.append(", "); + // Read value length + long value_length = buffer.getPackedLong(); + + Json_Value jsonValue = JsonConversion.parse_value(buffer.getUint8(), + buffer, + value_length - 1, + charsetName); + buffer.forward((int) value_length - 1); + // Read value + if (jsonValue.m_type == Json_enum_type.ERROR) { + throw new IllegalArgumentException("parsing json value"); + } + StringBuilder jsonBuilder = new StringBuilder(); + jsonValue.toJsonString(jsonBuilder, charsetName); + builder.append(jsonBuilder); + } + + // Print closing parenthesis + if (!buffer.hasRemaining() || operation_names.get(diff_i + 1) != operation_names.get(diff_i)) { + builder.append(")"); + } + + if (buffer.hasRemaining()) { + builder.append(", "); + } + diff_i++; + } + + return builder; + } + + private static String json_diff_operation_name(int operationType, int last_path_char) { + switch (operationType) { + case DIFF_OPERATION_REPLACE: + return "JSON_REPLACE"; + case DIFF_OPERATION_INSERT: + if (last_path_char == ']') { + return "JSON_ARRAY_INSERT"; + } else { + return "JSON_INSERT"; + } + case DIFF_OPERATION_REMOVE: + return "JSON_REMOVE"; + } + throw new IllegalArgumentException("illeagal operationType : " + operationType); + } +} diff --git a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogBuffer.java b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogBuffer.java index be61c99f..408eaa4e 100644 --- a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogBuffer.java +++ b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogBuffer.java @@ -1484,7 +1484,9 @@ public class LogBuffer { for (int bit = 0; bit < len; bit += 8) { int flag = ((int) buf[pos++]) & 0xff; - if (flag == 0) continue; + if (flag == 0) { + continue; + } if ((flag & 0x01) != 0) bitmap.set(bit); if ((flag & 0x02) != 0) bitmap.set(bit + 1); if ((flag & 0x04) != 0) bitmap.set(bit + 2); diff --git a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogDecoder.java b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogDecoder.java index 639e9875..41739cf8 100644 --- a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogDecoder.java +++ b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogDecoder.java @@ -372,6 +372,14 @@ public final class LogDecoder { header.putGtid(context.getGtidSet(), gtidLogEvent); return event; } + case LogEvent.PARTIAL_UPDATE_ROWS_EVENT: { + RowsLogEvent event = new UpdateRowsLogEvent(header, buffer, descriptionEvent, true); + /* updating position in context */ + logPosition.position = header.getLogPos(); + event.fillTable(context); + header.putGtid(context.getGtidSet(), gtidLogEvent); + return event; + } case LogEvent.GTID_LOG_EVENT: case LogEvent.ANONYMOUS_GTID_LOG_EVENT: { GtidLogEvent event = new GtidLogEvent(header, buffer, descriptionEvent); diff --git a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogEvent.java b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogEvent.java index 8a5957d0..41208516 100644 --- a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogEvent.java +++ b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogEvent.java @@ -169,9 +169,15 @@ public abstract class LogEvent { /* Prepared XA transaction terminal event similar to Xid */ public static final int XA_PREPARE_LOG_EVENT = 38; + /** + * Extension of UPDATE_ROWS_EVENT, allowing partial values according to + * binlog_row_value_options. + */ + public static final int PARTIAL_UPDATE_ROWS_EVENT = 39; + // mariaDb 5.5.34 /* New MySQL/Sun events are to be added right above this comment */ - public static final int MYSQL_EVENTS_END = 39; + public static final int MYSQL_EVENTS_END = 49; public static final int MARIA_EVENTS_BEGIN = 160; /* New Maria event numbers start from here */ @@ -361,6 +367,8 @@ public abstract class LogEvent { return "Anonymous_Gtid"; case PREVIOUS_GTIDS_LOG_EVENT: return "Previous_gtids"; + case PARTIAL_UPDATE_ROWS_EVENT: + return "Update_rows_partial"; default: return "Unknown"; /* impossible */ } diff --git a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/FormatDescriptionLogEvent.java b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/FormatDescriptionLogEvent.java index 8492e2d7..a14b9111 100644 --- a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/FormatDescriptionLogEvent.java +++ b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/FormatDescriptionLogEvent.java @@ -211,6 +211,7 @@ public final class FormatDescriptionLogEvent extends StartLogEventV3 { postHeaderLen[TRANSACTION_CONTEXT_EVENT - 1] = TRANSACTION_CONTEXT_HEADER_LEN; postHeaderLen[VIEW_CHANGE_EVENT - 1] = VIEW_CHANGE_HEADER_LEN; postHeaderLen[XA_PREPARE_LOG_EVENT - 1] = XA_PREPARE_HEADER_LEN; + postHeaderLen[PARTIAL_UPDATE_ROWS_EVENT - 1] = ROWS_HEADER_LEN_V2; // mariadb 10 postHeaderLen[ANNOTATE_ROWS_EVENT - 1] = ANNOTATE_ROWS_HEADER_LEN; diff --git a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/QueryLogEvent.java b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/QueryLogEvent.java index f6e0459c..c732da99 100644 --- a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/QueryLogEvent.java +++ b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/QueryLogEvent.java @@ -1,6 +1,7 @@ package com.taobao.tddl.dbsync.binlog.event; import java.io.IOException; +import java.math.BigInteger; import java.nio.charset.Charset; import com.taobao.tddl.dbsync.binlog.CharsetConversion; @@ -285,12 +286,12 @@ public class QueryLogEvent extends LogEvent { * type, * sql_mode */ - + 1 + 1 + 255 /* - * type, - * length - * , - * catalog - */ + + 1 + 1 + 255/* + * type, + * length + * , + * catalog + */ + 1 + 4 /* * type, * auto_increment @@ -330,14 +331,32 @@ public class QueryLogEvent extends LogEvent { * MariaDb type, * sec_part of NOW() */ - + 1 + (MAX_DBS_IN_EVENT_MTS * (1 + NAME_LEN)) + 3 + 1 + 32 * 3 - + 1 + 60/* - * type , - * user_len , - * user , - * host_len , - * host - */); + + 1 + (MAX_DBS_IN_EVENT_MTS * (1 + NAME_LEN)) + 3 /* + * type + * , + * microseconds + */+ 1 + 32 + * 3 + 1 + 60/* + * type , + * user_len + * , user , + * host_len + * , host + */) + + 1 + 1 /* + * type, + * explicit_def + * ..ts + */+ 1 + 8 /* + * type, + * xid + * of + * DDL + */+ 1 + 2 /* + * type + * , + * default_collation_for_utf8mb4_number + */+ 1 /* sql_require_primary_key */; /** * Fixed data part: *