Skip to content

[BugFix] Never raise a strict cast overflow error from a null row's data#74903

Merged
kangkaisen merged 2 commits into
StarRocks:mainfrom
trueeyu:allow_exception
Jun 17, 2026
Merged

[BugFix] Never raise a strict cast overflow error from a null row's data#74903
kangkaisen merged 2 commits into
StarRocks:mainfrom
trueeyu:allow_exception

Conversation

@trueeyu

@trueeyu trueeyu commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Why I'm doing:

A strict-mode numeric narrowing cast (the AllowThrowException path of UNARY_FN_CAST_VALID,
e.g. cast(x AS INT) when an overflow must error instead of becoming NULL) evaluated the overflow
check on every row of the input, including null rows. The data stored in a null row's slot
is undefined: when a producer leaves garbage there, the check sees an out-of-range value and throws
a spurious exception, failing a query that should succeed — the row is NULL and its value is
irrelevant.

What I'm doing:

  • Add NullAwareInputCheckUnaryFunction (be/src/exprs/unary_function.h). For a nullable input it
    converts all rows but only treats a non-null row's overflow as an error; a null row's data
    can never be the cause of an exception. The hot loop stays branchless and vectorizable (convert
    every row, detect overflow with the non-throwing check AND-ed with the not-null mask and
    OR-accumulated); the throwing check runs only on a cold path entered when a genuine non-null row
    overflows.
  • Wire the strict (AllowThrowException) branch of UNARY_FN_CAST_VALID to use it, passing the
    non-throwing NumberCheck for hot-loop detection and NumberCheckWithThrowException for the cold
    error path.

Bonus: the new implementation is ~1.4-1.6x faster than the old one across nullable/non-nullable
shapes — it avoids materializing and scanning a separate null-mask column and keeps the loop
vectorizable (no per-row branch, no throw in the hot path). Benchmark numbers are in the bench file.

What type of PR is this:

  • BugFix
  • Feature
  • Enhancement
  • Refactor
  • UT
  • Doc
  • Tool

Does this PR entail a change in behavior?

  • Yes, this PR will result in a change in behavior.
  • No, this PR will not result in a change in behavior.

If yes, please specify the type of change:

  • Interface/UI changes: syntax, type conversion, expression evaluation, display information
  • Parameter changes: default values, similar parameters but with different default values
  • Policy changes: use new policy to replace old one, functionality automatically enabled
  • Feature removed
  • Miscellaneous: upgrade & downgrade compatibility, etc.

Checklist:

  • I have added test cases for my bug fix or my new feature
  • This pr needs user documentation (for new or modified features or behaviors)
    • I have added documentation for my new feature or new function
    • This pr needs auto generate documentation
  • This is a backport pr

Bugfix cherry-pick branch check:

  • I have checked the version labels which the pr will be auto-backported to the target branch
    • 4.1
    • 4.0
    • 3.5

Copilot AI 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.

Pull request overview

Fixes a correctness bug in the native Parquet dictionary decoder where NULL rows could leave their backing data slots uninitialized, which can trigger false range-check failures during narrowing casts (e.g., under sql_mode='ALLOW_THROW_EXCEPTION'). The change ensures deterministic zero-filling for the null-heavy decode paths and adds both SQL and BE-unit regressions to guard against reintroduction.

Changes:

  • Zero-initialize newly grown buffers in dict decode-with-nulls paths by switching from resize_uninitialized() to resize().
  • Add a deterministic BE unit test that poisons the destination buffer and verifies NULL slots are zero-filled (covers sparse and filter-forwarding branches).
  • Add a Hive SQL regression reproducer for the original cast failure scenario.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
be/src/formats/parquet/encoding_dict.h Zero-fills nullable dict decode output slots to prevent uninitialized reads during casts; adds a test hook to control dict-size threshold.
be/test/formats/parquet/dict_encoding_test.cpp Refactors existing tests slightly and adds a regression test validating NULL-slot zero-fill behavior.
test/sql/test_hive/T/test_hive_parquet_dict_null_cast New SQL test that exercises dict-encoded, mostly-NULL Parquet data narrowed to INT under throw-checking mode.
test/sql/test_hive/R/test_hive_parquet_dict_null_cast Expected results for the new SQL test.
Comments suppressed due to low confidence (1)

be/test/formats/parquet/dict_encoding_test.cpp:82

  • The RleEncoder does not flush pending state on scope exit (it has no destructor calling Flush), so the last run may not be written to fs. This can make the test’s encoded stream incomplete and potentially hide regressions if the decoded count changes. Call encoder.Flush() after the loop to ensure the backing buffer contains all encoded values.
        RleEncoder<DICT_CXX_TYPE> encoder(&fs, 32);
        for (size_t i = 0; i < 4096; ++i) {
            encoder.Put(i % 9 + 1, 10);
        }
    }

Comment thread be/test/formats/parquet/dict_encoding_test.cpp Outdated
@trueeyu trueeyu marked this pull request as ready for review June 16, 2026 11:08
@CelerData-Reviewer

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: afc1ce85e7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread be/src/formats/parquet/encoding_dict.h Outdated

@stdpain stdpain 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.

I suggest a better fix: Call NullableColumn::fill_null_with_default before importing the side and handling the cast. This change will not solve the root cause, because OLAP scans or columns calculated from an expression will also exhibit the same behavior.

@trueeyu

trueeyu commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

I suggest a better fix: Call NullableColumn::fill_null_with_default before importing the side and handling the cast. This change will not solve the root cause, because OLAP scans or columns calculated from an expression will also exhibit the same behavior.

I split the changes into two PRs. This one fixes the direct root cause. The next PR will address the cast issue after passing performance tests.

dirtysalt
dirtysalt previously approved these changes Jun 16, 2026

@dirtysalt dirtysalt 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.

LGTM! 🚀

@trueeyu trueeyu marked this pull request as draft June 16, 2026 13:38
trueeyu added a commit to trueeyu/starrocks that referenced this pull request Jun 17, 2026
A strict-mode numeric narrowing cast evaluated its overflow check on every
row, including null rows whose data slot is undefined. When a producer left
garbage there (e.g. the Parquet dictionary decoder), the check saw an
out-of-range value and threw a spurious exception, failing a query that
should succeed.

Fix it at the cast layer: NullAwareInputCheckUnaryFunction only treats a
non-null row's overflow as an error, so a null row's data can never raise an
exception regardless of its source. The hot loop stays branchless and
vectorizable (convert all rows, detect overflow via the non-throwing check
masked by the not-null flag and OR-accumulated); the throwing check runs only
on a cold path. This is an alternative to StarRocks#74903 and is also ~1.4-1.6x faster.

Add unit tests and a microbenchmark, plus the SQLTester cases from StarRocks#74903.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@trueeyu trueeyu changed the title [BugFix] Zero-fill null rows' data slots in Parquet dictionary decoder [BugFix] Never raise a strict cast overflow error from a null row's data Jun 17, 2026
@trueeyu trueeyu requested a review from Copilot June 17, 2026 07:55

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment thread be/src/bench/cast_overflow_check_bench.cpp
Comment thread be/src/bench/cast_overflow_check_bench.cpp Outdated
Comment thread test/sql/test_hive/T/test_hive_parquet_dict_null_cast Outdated
A strict-mode numeric narrowing cast evaluated its overflow check on every
row, including null rows whose data slot is undefined. When a producer left
garbage there (e.g. the Parquet dictionary decoder), the check saw an
out-of-range value and threw a spurious exception, failing a query that
should succeed.

Fix it at the cast layer: NullAwareInputCheckUnaryFunction only treats a
non-null row's overflow as an error, so a null row's data can never raise an
exception regardless of its source. The hot loop stays branchless and
vectorizable (convert all rows, detect overflow via the non-throwing check
masked by the not-null flag and OR-accumulated); the throwing check runs only
on a cold path. This is an alternative to StarRocks#74903 and is also ~1.4-1.6x faster.

Add unit tests and a microbenchmark, plus the SQLTester cases from StarRocks#74903.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kangkaisen kangkaisen disabled auto-merge June 17, 2026 11:40
@kangkaisen kangkaisen merged commit 5847dd7 into StarRocks:main Jun 17, 2026
99 of 105 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

@Mergifyio backport branch-4.0

@github-actions github-actions Bot removed the 4.0 label Jun 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

@Mergifyio backport branch-3.5

@mergify

mergify Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

backport branch-4.0

✅ Backports have been created

Details

Cherry-pick of 5847dd7 has failed:

On branch mergify/bp/branch-4.0/pr-74903
Your branch is up to date with 'origin/branch-4.0'.

You are currently cherry-picking commit 5847dd7825.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:
	new file:   be/src/bench/cast_overflow_check_bench.cpp
	modified:   be/src/exprs/cast_expr.cpp
	modified:   be/src/exprs/unary_function.h
	modified:   be/test/CMakeLists.txt
	new file:   be/test/exprs/unary_function_test.cpp
	new file:   test/sql/test_hive/R/test_hive_parquet_dict_null_cast
	new file:   test/sql/test_hive/T/test_hive_parquet_dict_null_cast

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   be/src/bench/CMakeLists.txt

To fix up this pull request, you can check it out locally. See documentation: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally

@github-actions github-actions Bot removed the 3.5 label Jun 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

@Mergifyio backport branch-4.1

@github-actions github-actions Bot removed the 4.1 label Jun 17, 2026
@mergify

mergify Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

backport branch-3.5

✅ Backports have been created

Details

Cherry-pick of 5847dd7 has failed:

On branch mergify/bp/branch-3.5/pr-74903
Your branch is up to date with 'origin/branch-3.5'.

You are currently cherry-picking commit 5847dd7825.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:
	new file:   be/src/bench/cast_overflow_check_bench.cpp
	modified:   be/src/exprs/cast_expr.cpp
	modified:   be/src/exprs/unary_function.h
	modified:   be/test/CMakeLists.txt
	new file:   be/test/exprs/unary_function_test.cpp
	new file:   test/sql/test_hive/R/test_hive_parquet_dict_null_cast
	new file:   test/sql/test_hive/T/test_hive_parquet_dict_null_cast

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   be/src/bench/CMakeLists.txt

To fix up this pull request, you can check it out locally. See documentation: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally

@mergify

mergify Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

backport branch-4.1

✅ Backports have been created

Details

Cherry-pick of 5847dd7 has failed:

On branch mergify/bp/branch-4.1/pr-74903
Your branch is up to date with 'origin/branch-4.1'.

You are currently cherry-picking commit 5847dd7825.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:
	new file:   be/src/bench/cast_overflow_check_bench.cpp
	modified:   be/src/exprs/cast_expr.cpp
	modified:   be/src/exprs/unary_function.h
	modified:   be/test/CMakeLists.txt
	new file:   be/test/exprs/unary_function_test.cpp
	new file:   test/sql/test_hive/R/test_hive_parquet_dict_null_cast
	new file:   test/sql/test_hive/T/test_hive_parquet_dict_null_cast

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   be/src/bench/CMakeLists.txt

To fix up this pull request, you can check it out locally. See documentation: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally

trueeyu added a commit that referenced this pull request Jun 17, 2026
…ata (#74903)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wanpengfei-git <wanpengfei91@163.com>
(cherry picked from commit 5847dd7)
Signed-off-by: trueeyu <lxhhust350@qq.com>

# Conflicts:
#	be/src/bench/CMakeLists.txt
trueeyu added a commit that referenced this pull request Jun 17, 2026
…ata (#74903)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wanpengfei-git <wanpengfei91@163.com>
(cherry picked from commit 5847dd7)
Signed-off-by: trueeyu <lxhhust350@qq.com>

# Conflicts:
#	be/src/bench/CMakeLists.txt
trueeyu added a commit that referenced this pull request Jun 17, 2026
…ata (#74903)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wanpengfei-git <wanpengfei91@163.com>
(cherry picked from commit 5847dd7)
Signed-off-by: trueeyu <lxhhust350@qq.com>

# Conflicts:
#	be/src/bench/CMakeLists.txt
trueeyu added a commit that referenced this pull request Jun 17, 2026
…ata (#74903)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wanpengfei-git <wanpengfei91@163.com>
(cherry picked from commit 5847dd7)
Signed-off-by: trueeyu <lxhhust350@qq.com>

# Conflicts:
#	be/src/bench/CMakeLists.txt
trueeyu added a commit that referenced this pull request Jun 17, 2026
Keep the branch-3.5 bench list and add only the new
cast_overflow_check_bench entry introduced by #74903; the other
bench targets from main do not exist on branch-3.5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
trueeyu added a commit that referenced this pull request Jun 17, 2026
…ata (#74903)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wanpengfei-git <wanpengfei91@163.com>
trueeyu added a commit that referenced this pull request Jun 17, 2026
…ata (#74903)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wanpengfei-git <wanpengfei91@163.com>
wanpengfei-git added a commit that referenced this pull request Jun 17, 2026
…ata (backport #74903) (#74959)

Co-authored-by: trueeyu <lxhhust350@qq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wanpengfei-git <wanpengfei91@163.com>
wanpengfei-git added a commit that referenced this pull request Jun 17, 2026
…ata (backport #74903) (#74957)

Co-authored-by: trueeyu <lxhhust350@qq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wanpengfei-git <wanpengfei91@163.com>
wanpengfei-git added a commit that referenced this pull request Jun 17, 2026
…ata (backport #74903) (#74958)

Co-authored-by: trueeyu <lxhhust350@qq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wanpengfei-git <wanpengfei91@163.com>
wanpengfei-git added a commit that referenced this pull request Jun 26, 2026
…ata (backport #74903) (backport #74958) (#75362)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: trueeyu <lxhhust350@qq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wanpengfei-git <wanpengfei91@163.com>
OliLay pushed a commit to OliLay/starrocks that referenced this pull request Jun 30, 2026
…ata (StarRocks#74903)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wanpengfei-git <wanpengfei91@163.com>
Signed-off-by: Oliver Layer <o.layer@celonis.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants