Skip to content

Add tests for error paths in distributed executor and query executor#83

Merged
poyrazK merged 11 commits into
mainfrom
feature/testing-improvements
May 15, 2026
Merged

Add tests for error paths in distributed executor and query executor#83
poyrazK merged 11 commits into
mainfrom
feature/testing-improvements

Conversation

@poyrazK
Copy link
Copy Markdown
Owner

@poyrazK poyrazK commented May 14, 2026

Summary

  • Add tests for bloom filter skip on RIGHT JOIN (verifies BloomFilterPush RPC is skipped for outer joins)
  • Add tests for ShuffleFragment RPC failure error propagation
  • Add tests for SELECT error propagation from data nodes
  • Add tests for JOIN error paths (non-equality, missing table, > comparison)
  • Add tests for SELECT without FROM clause (parser returns nullptr)
  • Add test for unlock on never-locked RID
  • Add test for buffer pool double-unpin (pin_count already zero)
  • Add test for columnar table read_batch out-of-bounds
  • Add test for BloomFilter corrupted serialization handling
  • Add ThrowingVectorizedScanOperator helper class for future exception path testing

Test plan

  • Build all test targets
  • Run distributed_executor_tests (60 tests)
  • Run query_executor_tests (129 tests)
  • Run lock_manager_tests (20 tests)
  • Run buffer_pool_tests (16 tests)
  • Run columnar_table_tests (13 tests)
  • Run bloom_filter_tests (11 tests)
  • Run parser_tests (86 tests)

Summary by CodeRabbit

  • Tests
    • Added unit tests covering corrupted-serialization resilience; double-unpin behavior in buffer management; read-batch start beyond table bounds; RPC/error propagation and join-validation in distributed execution; bloom-filter optimization behavior across join types; unlock behavior for never-locked entries; parser handling of SELECT without FROM; join negative-paths and exception handling in vectorized operators.

Review Change Stack

poyrazK added 8 commits May 14, 2026 16:11
- DistributedExecutorWithNodesTests.Join_NonEqualityCondition_ReturnsError:
  tests that SELECT * FROM t1 JOIN t2 ON t1.id > t2.id returns error
  "Shuffle Join requires equality join condition" in distributed mode

- QueryExecutorTests.NonEqualityJoin_ReturnsError:
  tests that non-equality JOIN returns error in local executor mode
  when NestedLoopJoin is not implemented
- InnerJoinShuffle_EnablesBloomFilter: verifies shuffle join path
  executes by counting ShuffleFragment RPC calls
- RightJoinShuffle_SkipsBloomFilter: verifies BloomFilterPush RPC is
  skipped for RIGHT JOIN (is_outer_join=true at line 311)
- ThrowingVectorizedScanOperator helper class for future injection
  testing; documents expected error format for three exception types
  (out_of_range, std::exception, catch(...))
- NonEqualityJoin_GreaterThan_ReturnsError: tests > condition in JOIN
  (line 1297 in query_executor.cpp - NestedLoopJoin not implemented)
- JoinTableNotFound_ReturnsError: tests JOIN when join table doesn't
  exist in catalog (line 1223-1224 - build_plan returns nullptr)
- SelectErrorFromNode_ReturnsError: verifies error handling when
  ExecuteFragment returns success=false. Exercises all_success=false
  path at line 611 leading to res.set_error(errors) at line 953.
- ShuffleFragmentFailure_ReturnsError: verifies error when
  ShuffleFragment returns success=false (line 268). Error message
  "Shuffle failed on node: " + reply.error_msg is constructed.
- UnlockNeverLocked_ReturnsFalse: verifies unlock() returns false
  when RID not found in lock_table_ (line 117 in lock_manager.cpp)
- SelectWithoutFrom: verifies parser returns nullptr for SELECT
  without FROM clause (line 171-175)
- CorruptedSerialization_InertNoCrash: verifies BloomFilter
  handles invalid serialized data without crashing (line 92-112
  validation path with large header values)
- DoubleUnpin_ReturnsFalse: verifies unpin_page() returns false
  when pin_count_ is already zero (line 130 in buffer_pool_manager.cpp)
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

📝 Walkthrough

Walkthrough

Adds 12 unit tests across storage, parser, distributed executor, and query executor test suites to validate malformed serialization handling, boundary checks, error propagation, join-predicate validation, and operator exception handling; no production APIs changed.

Changes

Core Unit Test Coverage

Layer / File(s) Summary
Storage layer boundary conditions
tests/bloom_filter_test.cpp, tests/buffer_pool_tests.cpp, tests/columnar_table_tests.cpp, tests/lock_manager_tests.cpp
BloomFilter handles malformed serialized data inertly; BufferPool rejects double-unpin; ColumnarTable::read_batch fails when start_row is beyond row_count; LockManager::unlock returns false for unacquired RIDs.
SQL parser validation
tests/parser_tests.cpp
Parser returns nullptr for SELECT statements missing a FROM clause.
Distributed executor join and error paths
tests/distributed_executor_tests.cpp
ShuffleFragment RPC failures propagate errors to the client; non-equality JOIN predicates are rejected (error mentions “equality”); INNER JOIN triggers shuffle + BloomFilterPush; RIGHT JOIN skips bloom-filter optimization; ExecuteFragment errors from nodes propagate to the caller.
Query executor error handling
tests/query_executor_tests.cpp
JOIN plan-build failures for non-equi predicates and missing tables; test-only ThrowingVectorizedScanOperator simulates std::out_of_range, std::exception/std::runtime_error, and unknown exceptions to validate error-formatting and propagation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • poyrazK/cloudSQL#67: Overlapping distributed_executor_tests additions that exercise shuffle/join RPC behavior and bloom-filter/shuffle interactions.
  • poyrazK/cloudSQL#26: Related query_executor_tests changes covering JOIN negative-paths and executor error handling.
  • poyrazK/cloudSQL#25: Related BloomFilter serialization/deserialization test coverage that this PR’s corrupted-serialization test extends.

Poem

🐰 A hop, a sniff, through failing bytes I prance—
I test the bounds, the joins, give errors a glance.
When corrupt bits tumble, the bloom stays calm and still;
When joins misbehave, the planner says its fill.
Hooray for tests that keep the code in chance!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.88% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately and specifically describes the main objective: adding tests for error paths in distributed executor and query executor.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/testing-improvements

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@tests/distributed_executor_tests.cpp`:
- Around line 1386-1434: The test never asserts that BloomFilterPush is actually
invoked; add a counter for BloomFilterPush similar to shuffle_call_count (e.g.,
std::atomic<int> bloom_push_count{0}) and replace the BloomFilterPush handlers
(servers_[0]->set_handler/servers_[1]->set_handler with
network::RpcType::BloomFilterPush) with a counting handler that increments
bloom_push_count and calls send_success_reply(fd); after exec_->execute check
EXPECT_GE(bloom_push_count.load(), 1) to ensure the bloom-filter push path was
exercised (keep existing ShuffleFragment counting unchanged).
- Around line 585-599: The test only installs the ShuffleFragment failure
handler on servers_[0], so if servers_[1] is chosen by the test it has no
handler and causes unrelated failures; update the setup to register the same
failure handler for every server that was started (e.g., iterate over servers_
and call set_handler for network::RpcType::ShuffleFragment with the existing
lambda that constructs a QueryResultsReply with success=false and
error_msg="shard rejected shuffle", sets resp_h.type to
network::RpcType::QueryResults and writes the header+payload to fd) so all data
nodes will consistently return the intended shuffle error.

In `@tests/query_executor_tests.cpp`:
- Around line 1553-1608: The three tests are placeholders that never exercise
ThrowingVectorizedScanOperator/VectorizedOperator::next_batch, so implement each
test by instantiating ThrowingVectorizedScanOperator with the appropriate
ThrowType (OutOfRange, StdException, Unknown), invoking it through the same path
query_executor uses (inject the operator into the executor/plan or call
operator.next_batch via the same wrapper used in query_executor.cpp error
handling), and assert the resulting error message text matches expectations: for
OutOfRange assert it contains "vector access error in next_batch" and batch
context, for StdException assert it contains "next_batch error: " + e.what(),
and for Unknown assert it equals "next_batch error: unknown exception type";
replace the SUCCEED() calls in tests VectorizedScan_OutOfRangeException,
VectorizedScan_StdException, and VectorizedScan_UnknownException with these
concrete invocations and assertions using the existing TestEnvironment and
executor hooks.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: ac6de54a-2f6a-48e1-9e57-a04af9a0ca6e

📥 Commits

Reviewing files that changed from the base of the PR and between f2fae18 and 3b7e76b.

📒 Files selected for processing (7)
  • tests/bloom_filter_test.cpp
  • tests/buffer_pool_tests.cpp
  • tests/columnar_table_tests.cpp
  • tests/distributed_executor_tests.cpp
  • tests/lock_manager_tests.cpp
  • tests/parser_tests.cpp
  • tests/query_executor_tests.cpp

Comment thread tests/distributed_executor_tests.cpp Outdated
Comment thread tests/distributed_executor_tests.cpp Outdated
Comment thread tests/query_executor_tests.cpp
- ReadBatch_StartRowBeyondTable: verifies read_batch() returns
  false when start_row >= row_count_ (line 124 in columnar_table.cpp)
@poyrazK poyrazK force-pushed the feature/testing-improvements branch from 3b7e76b to f22b37b Compare May 14, 2026 17:31
distributed_executor_tests.cpp:
- InnerJoinShuffle_ExecutesShufflePath: add bloom_filter_push_count atomic
  and counting handler to verify BloomFilterPush is invoked (count >= 1)
- ShuffleFragmentFailure_ReturnsError: register failure_h on ALL servers_
  via for loop instead of only servers_[0] to ensure consistent behavior

query_executor_tests.cpp:
- Replace SUCCEED() placeholders with concrete ThrowingVectorizedScanOperator
  tests that call next_batch(), replicate query_executor exception handling,
  and assert expected error message substrings for all three throw types
@poyrazK poyrazK force-pushed the feature/testing-improvements branch from 6d1b71f to d2cb87c Compare May 15, 2026 11:34
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
tests/query_executor_tests.cpp (1)

1594-1601: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

These tests still mirror exception formatting instead of validating QueryExecutor’s actual catch path.

The new tests manually build error_msg in local catch blocks, so a regression in query_executor.cpp error handling can go undetected. Please route ThrowingVectorizedScanOperator through the same executor path (or a shared formatter used by production code) to make this coverage authoritative.

Also applies to: 1625-1630, 1652-1657

🤖 Prompt for 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.

In `@tests/query_executor_tests.cpp` around lines 1594 - 1601, The tests currently
catch exceptions locally and build error_msg themselves; instead instantiate
ThrowingVectorizedScanOperator and run it through QueryExecutor's actual
execution path (e.g., call QueryExecutor::execute or the public method that
invokes the operator and performs the catch/formatting) so the production
catch/formatter is exercised; remove the local try/catch around op.next_batch
and assert on the error string returned/recorded by QueryExecutor (or use the
shared formatter function used by QueryExecutor if one exists) to ensure the
test validates the real error-handling behavior for
ThrowingVectorizedScanOperator.
🤖 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 `@tests/query_executor_tests.cpp`:
- Around line 603-606: The test currently only checks execute_sql(env.executor,
"SELECT * FROM t1 JOIN t2 ON t1.id > t2.val") for a false success flag which can
hide unrelated failures; update the assertion to also verify that res.error() is
non-empty and includes planner/build-plan-related wording (similar to
neighboring JOIN negative tests) so the test asserts the failure reason comes
from the planner/unsupported join, referencing the execute_sql call result
variable res and env.executor to locate the code.

---

Duplicate comments:
In `@tests/query_executor_tests.cpp`:
- Around line 1594-1601: The tests currently catch exceptions locally and build
error_msg themselves; instead instantiate ThrowingVectorizedScanOperator and run
it through QueryExecutor's actual execution path (e.g., call
QueryExecutor::execute or the public method that invokes the operator and
performs the catch/formatting) so the production catch/formatter is exercised;
remove the local try/catch around op.next_batch and assert on the error string
returned/recorded by QueryExecutor (or use the shared formatter function used by
QueryExecutor if one exists) to ensure the test validates the real
error-handling behavior for ThrowingVectorizedScanOperator.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: e5bff0fc-fa10-4a07-91f6-e9ef778ffcc9

📥 Commits

Reviewing files that changed from the base of the PR and between 3b7e76b and eb4bd85.

📒 Files selected for processing (4)
  • tests/bloom_filter_test.cpp
  • tests/columnar_table_tests.cpp
  • tests/distributed_executor_tests.cpp
  • tests/query_executor_tests.cpp
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/bloom_filter_test.cpp
  • tests/columnar_table_tests.cpp
  • tests/distributed_executor_tests.cpp

Comment on lines +603 to +606
// JOIN with > condition - cannot use HashJoin, NestedLoopJoin not implemented
const auto res = execute_sql(env.executor, "SELECT * FROM t1 JOIN t2 ON t1.id > t2.val");
EXPECT_FALSE(res.success()) << "Non-equality JOIN with > should fail";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Strengthen the > JOIN failure assertion.

On Line 605, this test only checks !success; it can pass for unrelated failure reasons. Assert that the error is non-empty (and preferably contains planner/build-plan wording) like the neighboring negative JOIN tests.

🤖 Prompt for 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.

In `@tests/query_executor_tests.cpp` around lines 603 - 606, The test currently
only checks execute_sql(env.executor, "SELECT * FROM t1 JOIN t2 ON t1.id >
t2.val") for a false success flag which can hide unrelated failures; update the
assertion to also verify that res.error() is non-empty and includes
planner/build-plan-related wording (similar to neighboring JOIN negative tests)
so the test asserts the failure reason comes from the planner/unsupported join,
referencing the execute_sql call result variable res and env.executor to locate
the code.

Copy link
Copy Markdown
Owner Author

@poyrazK poyrazK left a comment

Choose a reason for hiding this comment

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

It's okay to merge

@poyrazK poyrazK merged commit 5911004 into main May 15, 2026
1 check passed
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.

1 participant