Sitelet https://github.com/apache/paimon/pull/10372
Skip to content

[cdc] Parse MySQL SET bitmaps as unsigned 64-bit in canal/aliyun - #10372

Open
jackylee-ch wants to merge 1 commit into
apache:masterfrom
jackylee-ch:cdc-set-bitmap-long
Open

jackylee-ch wants to merge 1 commit into
apache:masterfrom
jackylee-ch:cdc-set-bitmap-long

Conversation

@jackylee-ch

Copy link
Copy Markdown
Contributor

Purpose

CanalFieldParser and AliyunFieldParser decode a MySQL SET value from its
bitmap representation with Integer.parseInt(value). A MySQL SET column may
declare up to 64 members, so selecting a member at bit 31 or higher yields a
bitmap value greater than Integer.MAX_VALUE. Integer.parseInt then threw
NumberFormatException, failing the whole canal / aliyun-DTS sync job for an
otherwise valid row.

This parses the bitmap as a long and widens getSetValuesByIndex to match. The
decoding loop already operated in long terms (indexes != 0L, indexes % 2L,
indexes >>> 1), and the method's own comment already states the value is
"a bit string conversion from the long" — the parse was the only place still
narrowed to int. Values within the int range decode exactly as before.

Tests

CanalFieldParserSetTest / AliyunFieldParserSetTest decode a 40-member SET:
a high bit (2^32) and a low+high combination both resolve to the correct members,
and a small in-range value is unchanged. The high-bit cases throw
NumberFormatException before the change.

API and Format

No change.

Documentation

No change.

@JingsongLi JingsongLi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requirement fit: SUPPORTED for the Canal CDC path. Implementation: FINDINGS.

I ran both new SET test classes with JDK 8, -Pflink1 and the normal Maven checks: 6/6 passed. However, an additional test feeding a real Canal INSERT event into CanalRecordParser still fails for the 64th SET member; an unsigned-parsing control passes and produces [c63].

Scope note: AliyunFieldParser currently has no production callers; AliyunRecordParser.extractRowData does not invoke it. Its helper tests therefore do not establish that a DTS job is fixed. Please keep the claim/scope tied to the actual parser path, or demonstrate and exercise the missing Aliyun connection.

// mysql set type value can be filled with more than one, value is a bit string conversion
// from the long
int indexes = Integer.parseInt(value);
long indexes = Long.parseLong(value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Parse the SET bitmap as unsigned so the 64th member is supported. MySQL permits 64 SET members (https://dev.mysql.com/doc/refman/8.4/en/set.html). Canal's RowsLogBuffer uses getUlong64 for 8-byte SET values, and LogBuffer.getUlong64 returns BigInteger; LogEventConvert serializes it using String.valueOf, so selecting member 64 produces "9223372036854775808", not a negative signed-long string. This new Long.parseLong still throws NumberFormatException for that valid event, leaving the claimed wide-SET CDC failure unfixed at the upper boundary. I reproduced it through CanalRecordParser.buildSchema with mysqlType set('c0',...,'c63') and INSERT data s="9223372036854775808". Replacing this call with Long.parseUnsignedLong makes the same event decode to [c63]; the existing >>> shift loop already supports the resulting bits. Please use unsigned parsing and add actual-event tests for bit 63 and all 64 bits. Upstream encoding: https://github.com/alibaba/canal/blob/master/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/RowsLogBuffer.java#L868 and https://github.com/alibaba/canal/blob/master/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/LogBuffer.java#L996 .

CanalFieldParser decoded a MySQL SET value from its bitmap with a signed
parse, so a SET with 32+ members overflowed int, and even after widening to
long the 64th member (bit 63, decimal 2^63 > Long.MAX_VALUE) still overflowed
a signed long and threw NumberFormatException, failing the sync. Parse the
bitmap with Long.parseUnsignedLong so the full 64-member range decodes; the
decoding loop already shifts unsigned (>>>).

The production path is Canal (CanalRecordParser -> convertSet).
AliyunFieldParser carries the identical duplicated code and is fixed in
parallel so it stays correct for when it is wired to a record parser; its
unit test exercises the helper, not a DTS job.
@jackylee-ch jackylee-ch changed the title [cdc] Decode MySQL SET bitmaps wider than 31 bits in canal/aliyun [cdc] Parse MySQL SET bitmaps as unsigned 64-bit in canal/aliyun Oct 4, 2026
@jackylee-ch

Copy link
Copy Markdown
Contributor Author

Good catch. The 64th SET member sets bit 63 (decimal 2^63 > Long.MAX_VALUE), so even the signed-long parse threw NumberFormatException. Switched to Long.parseUnsignedLong, so the full 64-member range decodes — the loop already shifts unsigned (>>>). Added a SET(64) case asserting the 64th member resolves to [c63]; it fails on the signed parse.

On scope: the production path is Canal (CanalRecordParser → convertSet); I've tied the claim there. AliyunFieldParser is byte-identical duplicated code, fixed in parallel to stay correct for when it is wired to a record parser — its unit test exercises the helper, not a DTS job. Pushed in 50c03c1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants