Skip to content

dumpformat: fix corrupted DECIMAL values in dictionary-encoded parquet files - #70436

Merged
ti-chi-bot[bot] merged 2 commits into
pingcap:masterfrom
joechenrh:fix/parquet-decimal-dict-alias
Aug 12, 2026
Merged

ti-chi-bot[bot] merged 2 commits into
pingcap:masterfrom
joechenrh:fix/parquet-decimal-dict-alias

Conversation

@joechenrh

@joechenrh joechenrh commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: close #70365

Problem Summary:

IMPORT INTO silently writes wrong values for repeated dictionary-encoded Parquet DECIMAL columns. Only the first occurrence of a dictionary entry is correct; every later row referencing the same entry gets a different value (0.00 in the reported witness). The job reports success and ADMIN CHECK TABLE passes, because both validate the KVs produced after the value was already corrupted.

Root cause is buffer ownership:

  1. types.MyDecimal.FromParquetArray documents its input as disposable and does the radix conversion by overwriting the byte slice in place (the two's-complement negation and the long division both write into buf). The oversized-value fallback getStringFromParquetByte does the same.
  2. arrow-go's dictConverter.Fill/Copy fill decoded values by assigning dict[idx], i.e. only the slice header. Every row referencing the same dictionary entry shares one backing array.
  3. The DECIMAL setters passed the decoded slice straight into the conversion, so converting the first row rewrote the dictionary entry, and later rows decoded from the already-consumed bytes.

What changed and how does it work?

Copy each value into a buffer owned by the setter before converting. The column iterator drives setters sequentially (columnIterator is documented as not usable in parallel), so one reused buffer per column keeps the copy allocation-free — no per-row allocation is added.

Both byte-array DECIMAL setters (BYTE_ARRAY and FIXED_LEN_BYTE_ARRAY) go through the shared helper, so the fix covers both physical types and both the direct MyDecimal path and the string fallback path for oversized values.

AI has also checked other code paths, this is the only path need to be fixed.

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No need to test
    • I checked and no code files have been changed.

Side effects

  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Breaking backward compatibility

Documentation

  • Affects user behaviors
  • Contains syntax changes
  • Contains variable changes
  • Contains experimental features
  • Changes MySQL compatibility

Release note

Fix the issue that `IMPORT INTO` silently writes wrong values for repeated dictionary-encoded Parquet DECIMAL columns.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed decoding of dictionary-encoded decimal values, including positive, negative, variable-length, and fixed-length representations.
    • Prevented shared dictionary data from being modified during decimal conversion.
  • Tests

    • Added coverage for repeated dictionary-encoded decimals, including oversized values, across all decoded records.

setDatumFromDecimalByte consumes its input in place (radix conversion and
two's-complement negation both write into the byte slice). Values decoded from
a dictionary-encoded page alias the shared dictionary entry, so converting the
first row rewrote the dictionary bytes and every later row referencing the same
entry decoded from the already-consumed buffer, silently producing a wrong
value (0.00 in the reported witness).

Copy each value into a buffer owned by the setter before converting. The
column iterator drives setters sequentially, so one reused buffer per column
keeps the copy allocation-free.

close pingcap#70365

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ti-chi-bot ti-chi-bot Bot added do-not-merge/needs-tests-checked release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Aug 11, 2026
@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The decimal converter now copies Parquet byte-array inputs before conversion. A parser integration test covers repeated dictionary-encoded decimals in variable-length and fixed-length representations.

Changes

Decimal dictionary safety

Layer / File(s) Summary
Copy decimal bytes before conversion
pkg/dumpformat/parquetfile/type_converter.go
Added a reusable decimal byte setter and applied it to byte-array and fixed-length byte-array decimal conversion.
Validate repeated dictionary decimals
pkg/dumpformat/parquetfile/parser_test.go
Added coverage for 256 repeated dictionary-encoded decimal values, dictionary pages, and positive, negative, fixed-length, and oversized decimal string results.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

A rabbit copies bytes with care,
So shared dictionary values stay fair.
Each decimal keeps its shape,
Through every repeated escape.
“Hop!” says the test, “the results are there.”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes fix shared-buffer mutation for dictionary-encoded DECIMAL values and add coverage for both byte-array physical types and repeated values [#70365].
Out of Scope Changes check ✅ Passed The changes are limited to DECIMAL conversion ownership and focused regression coverage required by the linked issue.
Title check ✅ Passed The title clearly identifies the package and the fix for corrupted DECIMAL values in dictionary-encoded Parquet files.
Description check ✅ Passed The description includes the issue, problem summary, implementation details, unit test coverage, side effects, documentation checklist, and release note.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/dumpformat/parquetfile/parser_test.go`:
- Line 1156: Update the cleanup around the Parquet reader in the relevant test
to capture and assert the error returned by rdr.Close instead of discarding it.
Use the test’s existing assertion mechanism and ensure the cleanup runs after
the test body.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1a715999-f383-436b-a191-08b5e3292a75

📥 Commits

Reviewing files that changed from the base of the PR and between 31c9768 and 29a0f1e.

📒 Files selected for processing (2)
  • pkg/dumpformat/parquetfile/parser_test.go
  • pkg/dumpformat/parquetfile/type_converter.go

Comment thread pkg/dumpformat/parquetfile/parser_test.go Outdated
@codecov

codecov Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.4692%. Comparing base (31c9768) to head (8b83ed8).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@               Coverage Diff                @@
##             master     #70436        +/-   ##
================================================
- Coverage   76.3277%   73.4692%   -2.8586%     
================================================
  Files          2041       2081        +40     
  Lines        558363     589824     +31461     
================================================
+ Hits         426186     433339      +7153     
- Misses       131277     155412     +24135     
- Partials        900       1073       +173     
Flag Coverage Δ
integration 41.5633% <0.0000%> (+1.8946%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
dumpling 59.8974% <ø> (ø)
parser ∅ <ø> (∅)
br 46.6167% <ø> (-16.0923%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ingress-bot

Copy link
Copy Markdown

🔍 Starting code review for this PR...

@ingress-bot ingress-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This review was generated by AI and should be verified by a human reviewer.
Manual follow-up is recommended before merge.

Summary

  • Total findings: 4
  • Inline comments: 3
  • Summary-only findings (no inline anchor): 1
Findings (highest risk first)

🟡 [Minor] (3)

  1. repeatedByteArray and repeatedFixedLenByteArray duplicate the same generator body (pkg/dumpformat/parquetfile/parser_test.go:1099, pkg/dumpformat/parquetfile/parser_test.go:1110)
  2. Regression test misses the large-decimal string-fallback path that has the same shared-buffer bug (pkg/dumpformat/parquetfile/parser_test.go:1122, pkg/dumpformat/parquetfile/type_converter.go:72)
  3. Regression test misses the in-place-mutating fallback decimal path (pkg/dumpformat/parquetfile/parser_test.go:1165, pkg/dumpformat/parquetfile/type_converter.go:72)

🧹 [Nit] (1)

  1. newDecimalByteSetter breaks the file's get*Setter naming convention (pkg/dumpformat/parquetfile/type_converter.go:390)

Unanchored findings

🟡 [Minor] (1)

  1. repeatedByteArray and repeatedFixedLenByteArray duplicate the same generator body
    • Request: Collapse the two closures into one generic helper parameterized on the slice element type, or on any with a type-switch, and use it for all three Gen fields in this subtest.

Comment thread pkg/dumpformat/parquetfile/parser_test.go
Comment thread pkg/dumpformat/parquetfile/parser_test.go Outdated
Comment thread pkg/dumpformat/parquetfile/type_converter.go Outdated
… test

Values of maximumDecimalBytes or longer take getStringFromParquetByte, which
consumes its input in place just like FromParquetArray, so add a dictionary
encoded column that reaches it. Verified it decodes 0.00 from row 1 onwards
when the copy is removed.

Also rename newDecimalByteSetter to getDecimalByteSetter to match the other
setter factories in the file, and assert the error from closing the reader.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ti-chi-bot ti-chi-bot Bot added the approved label Aug 11, 2026
@D3Hunter

Copy link
Copy Markdown
Contributor

/cherry-pick release-nextgen-202603

@ti-chi-bot ti-chi-bot Bot added the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Aug 11, 2026
@ti-chi-bot

Copy link
Copy Markdown
Member

@D3Hunter: once the present PR merges, I will cherry-pick it on top of release-nextgen-202603 in the new PR and assign it to you.

Details

In response to this:

/cherry-pick release-nextgen-202603

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository.

@D3Hunter

Copy link
Copy Markdown
Contributor

/retest

@ti-chi-bot

ti-chi-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: D3Hunter, OliverS929

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot added lgtm and removed needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Aug 12, 2026
@ti-chi-bot

ti-chi-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

[LGTM Timeline notifier]

Timeline:

  • 2026-08-11 10:20:57.804628155 +0000 UTC m=+3128243.840723241: ☑️ agreed by D3Hunter.
  • 2026-08-12 03:12:51.07298183 +0000 UTC m=+3188957.109076886: ☑️ agreed by OliverS929.

@ti-chi-bot
ti-chi-bot Bot merged commit 143374f into pingcap:master Aug 12, 2026
34 checks passed
@ti-chi-bot

Copy link
Copy Markdown
Member

@D3Hunter: new pull request created to branch release-nextgen-202603: #70459.

Details

In response to this:

/cherry-pick release-nextgen-202603

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository.

@joechenrh

Copy link
Copy Markdown
Contributor Author

/cherry-pick release-8.5

@ti-chi-bot

Copy link
Copy Markdown
Member

@joechenrh: new pull request created to branch release-8.5: #70461.

Details

In response to this:

/cherry-pick release-8.5

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository.

joechenrh added a commit to ti-chi-bot/tidb that referenced this pull request Aug 12, 2026
… files (pingcap#70436)

The automated cherry-pick of pingcap#70436 dropped two files into
pkg/dumpformat/parquetfile, a package that does not exist on this
branch, and left the actual 8.5 code path untouched. This replaces it
with the equivalent fix for release-8.5.

Here the byte-array DECIMAL setters live in pkg/lightning/mydump and
convert through binaryToDecimalStr, which rewrites its input in place
when the value is negative. arrow-go's dictionary decoder assigns
dict[idx] to every row referencing an entry, so all those rows share one
backing array: converting the first occurrence negated the dictionary
entry, and later rows decoded the already-negated bytes as a positive
value. Positive values are unaffected on this branch because that path
only calls big.Int.SetBytes, which does not touch the input.

Copy each value into a buffer owned by the setter before converting. The
column iterator drives setters sequentially, so one reused buffer per
column keeps the copy allocation-free.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ti-chi-bot Bot pushed a commit that referenced this pull request Aug 12, 2026
ti-chi-bot Bot pushed a commit that referenced this pull request Aug 12, 2026
@joechenrh
joechenrh deleted the fix/parquet-decimal-dict-alias branch September 1, 2026 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved lgtm release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IMPORT INTO silently corrupts repeated dictionary-encoded Parquet DECIMAL values

5 participants