Skip to content

fix: Iceberg positional delete compare base64 encoded value with raw value#16592

Closed
PingLiuPing wants to merge 1 commit intofacebookincubator:mainfrom
PingLiuPing:lp_fix_iceberg_pos_delete
Closed

fix: Iceberg positional delete compare base64 encoded value with raw value#16592
PingLiuPing wants to merge 1 commit intofacebookincubator:mainfrom
PingLiuPing:lp_fix_iceberg_pos_delete

Conversation

@PingLiuPing
Copy link
Copy Markdown
Collaborator

When reading Iceberg positional delete files, the IcebergSplitReader optimizes away irrelevant delete files by comparing the delete file's upper_bounds statistic against the current split's starting row offset.
This PR fixes a bugs in that logic. Iceberg stores column statistics (lower_bounds / upper_bounds) in manifests as Base64-encoded, little-endian binary values (for long type). For a long column (such as the positional delete pos column), the value is an 8-byte little-endian integer, Base64-encoded.
Previously it passed the raw Base64 string directly to folly::to<uint64_t>(), which attempts to parse it as a decimal string. For example, the value CgAAAAAAAAA= should be decoded to integer 10, but folly::to<uint64_t>("CgAAAAAAAAA=") throws exception.
This PR fix this by decoding the upper bound first and then reinterpret the bytes as uint64_t and then apply folly::Endian::little() to convert from the iceberg spec specified little-endian encoding to host byte order before comparing against splitOffset_.

This fixes the issue prestodb/presto#27237

@PingLiuPing PingLiuPing requested review from Yuhta and mbasmanova March 2, 2026 16:07
@meta-cla meta-cla bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Mar 2, 2026
@netlify
Copy link
Copy Markdown

netlify bot commented Mar 2, 2026

Deploy Preview for meta-velox canceled.

Name Link
🔨 Latest commit d9ae324
🔍 Latest deploy log https://app.netlify.com/projects/meta-velox/deploys/69a5ca314c4ee100085218c2

@@ -920,7 +922,13 @@ TEST_F(HiveIcebergTest, skipDeleteFileByPositionUpperBound) {
makeFlatVector<int64_t>({0, 1, 2})})};
writeToFile(deleteFilePath->getPath(), deleteVectors);

// upperBound "2" is the max position in the delete file.
// upperBound "2" is the max position in the delete file. Iceberg stores
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.

@PingLiuPing Would you confirm that the test fails without the fix?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes,

unknown file: Failure
C++ exception with description "Exception: VeloxRuntimeError
Error Source: RUNTIME
Error Code: INVALID_STATE
Reason: Operator::getOutput failed for [operator: TableScan, plan node ID: 0]: Non-digit character found: "AgAAAAAAAAA="
Retriable: False
Function: operator()

"AgAAAAAAAAA=" is base64 encoded value.

uint64_t upperBound = 2;
auto upperBoundLE = folly::Endian::little(upperBound);
auto encodedUpperBound = encoding::Base64::encode(
folly::StringPiece(
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.

Can we use std::string_view?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks.

Comment on lines +31 to +32
static constexpr int32_t kPosId = 2'147'483'545;
static constexpr int32_t kFilePathId = 2'147'483'546;
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.

Where do these numbers come from?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

They are defined in https://iceberg.apache.org/spec/#position-delete-files, added a comment to explain the value.

@mbasmanova
Copy link
Copy Markdown
Contributor

/claude-review

@PingLiuPing PingLiuPing force-pushed the lp_fix_iceberg_pos_delete branch from f4e336c to c9481fe Compare March 2, 2026 17:24
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 2, 2026

✅ Claude Code Review

Requested by @mbasmanova


Summary

I've completed a thorough analysis of this PR fixing the Iceberg positional delete Base64 decoding issue.

Issues Found

🟡 Suggestion: Error Handling Enhancement

File: IcebergSplitReader.cpp:91-95
Issue: The Base64 decode operation in encoding::Base64::decode(iter->second) could potentially throw an exception, but there's no explicit error handling around it. While the VELOX_CHECK_EQ will catch size mismatches, malformed Base64 strings could still cause issues.
Suggested Fix: Consider wrapping the decode operation in a try-catch or using a Base64 decode variant that returns a status/optional to handle malformed input gracefully.

🟡 Suggestion: Const Correctness

File: IcebergSplitReader.cpp:95-96
Issue: The variable posDeleteUpperBound could be const since it's only written to once via std::memcpy and then only read.
Suggested Fix:

const uint64_t posDeleteUpperBound = folly::Endian::little(
    *reinterpret_cast<const uint64_t*>(decodedBound.data()));

🟡 Suggestion: Alignment Consideration

File: IcebergSplitReader.cpp:96-98
Issue: Using std::memcpy is the safe approach for unaligned access, but the comment doesn't explain why this is necessary (avoiding undefined behavior from unaligned access).
Suggested Fix: Add a comment explaining this protects against potential alignment issues:

// Use memcpy to avoid potential alignment issues with reinterpret_cast
std::memcpy(&posDeleteUpperBound, decodedBound.data(), sizeof(uint64_t));

🟢 Nitpick: Include Organization

File: IcebergSplitReader.cpp:19-21
Issue: The new includes (folly/lang/Bits.h and velox/common/encode/Base64.h) are added at the top but could be better organized with other folly includes grouped together.

Positive Observations

Correctness: The fix properly addresses the core issue by decoding Base64 before parsing as uint64_t, which is exactly what the Iceberg specification requires.

Endianness Handling: Proper use of folly::Endian::little() to handle the little-endian format specified by Iceberg.

Memory Safety: Appropriate use of std::memcpy to avoid alignment issues and undefined behavior.

Size Validation: The VELOX_CHECK_EQ ensures the decoded size matches expectations before proceeding.

Test Coverage: The test properly demonstrates the fix by encoding a test value using the same process (little-endian + Base64).

Code Organization: Constants moved to header file with clear naming (kPosId, kFilePathId) improving maintainability.

Boundary Case Handling: The logic correctly handles the case where upper bound equals split offset (should continue processing).

The fix is technically sound and addresses a real bug where Base64-encoded values were being incorrectly parsed as decimal strings, which would throw exceptions. The implementation follows Velox patterns and properly handles the Iceberg specification for storing statistics as Base64-encoded little-endian binary values.


ℹ️ About this response

This review was generated by Claude Code. It analyzed the PR diff and codebase to provide feedback.

Limitations:

  • Claude may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Available commands:

  • /claude-review - Request a code review
  • /claude-query <question> - Ask a question about the PR or codebase

@PingLiuPing PingLiuPing force-pushed the lp_fix_iceberg_pos_delete branch from c9481fe to d9ae324 Compare March 2, 2026 17:34
Copy link
Copy Markdown
Contributor

@mbasmanova mbasmanova left a comment

Choose a reason for hiding this comment

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

Thanks.

@mbasmanova mbasmanova added the ready-to-merge PR that have been reviewed and are ready for merging. PRs with this tag notify the Velox Meta oncall label Mar 2, 2026
@meta-codesync
Copy link
Copy Markdown

meta-codesync bot commented Mar 2, 2026

@kKPulla has imported this pull request. If you are a Meta employee, you can view this in D94940646.

Copy link
Copy Markdown
Collaborator

@czentgr czentgr left a comment

Choose a reason for hiding this comment

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

Thanks for the quick fix.

@meta-codesync
Copy link
Copy Markdown

meta-codesync bot commented Mar 3, 2026

@kKPulla merged this pull request in fb5b296.

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

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged ready-to-merge PR that have been reviewed and are ready for merging. PRs with this tag notify the Velox Meta oncall

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants