fix(dataset): Fix NoSuchColumnException in CompositeTable.getValue - #908
Conversation
Reviewer's GuideFixes CompositeTable.getValue to safely handle columns present in composite metadata but missing from some backing tables by returning ITable.NO_VALUE instead of propagating NoSuchColumnException, and adds regression tests in dataset and operation layers plus a changes.xml entry for issue #708. Sequence diagram for CompositeTable.getValue handling missing columns in InsertOperationsequenceDiagram
participant InsertOperation
participant CompositeTable
participant PartTable as ITable_part
participant Columns
InsertOperation->>CompositeTable: getValue(row, columnName)
CompositeTable->>PartTable: getRowCount()
Note right of CompositeTable: locate part backing row
CompositeTable->>CompositeTable: isMissingFromPart(columnName, PartTable)
CompositeTable->>Columns: getColumn(columnName, _metaData.getColumns())
Columns-->>CompositeTable: Column or null
CompositeTable->>Columns: getColumn(columnName, PartTable.getTableMetaData().getColumns())
Columns-->>CompositeTable: Column or null
alt column present in part
CompositeTable->>PartTable: getValue(relativeRow, columnName)
PartTable-->>CompositeTable: value
CompositeTable-->>InsertOperation: value
else column missing from part
CompositeTable-->>InsertOperation: ITable.NO_VALUE
end
alt value == ITable.NO_VALUE
InsertOperation->>InsertOperation: omit column from generated statement
else value supplied
InsertOperation->>InsertOperation: include column in generated statement
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Warning Review limit reached
Next review available in: 20 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthrough
ChangesCompositeTable optional column handling
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In CompositeTable.isMissingFromPart, you repeatedly scan both the composite and part metadata via Columns.getColumn on every getValue call; consider caching a column lookup map or reusing existing metadata structures to avoid repeated linear scans on hot paths.
- The new InsertOperationIT test asserts specific row ordering when reading back from EMPTY_TABLE without an explicit ORDER BY; to avoid potential flakiness across databases, consider either enforcing a deterministic ordering (e.g., via a primary key) or relaxing the assertions to be order-insensitive.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In CompositeTable.isMissingFromPart, you repeatedly scan both the composite and part metadata via Columns.getColumn on every getValue call; consider caching a column lookup map or reusing existing metadata structures to avoid repeated linear scans on hot paths.
- The new InsertOperationIT test asserts specific row ordering when reading back from EMPTY_TABLE without an explicit ORDER BY; to avoid potential flakiness across databases, consider either enforcing a deterministic ordering (e.g., via a primary key) or relaxing the assertions to be order-insensitive.
## Individual Comments
### Comment 1
<location path="src/test/java/org/dbunit/dataset/CompositeTableTest.java" line_range="93-102" />
<code_context>
+ connection.verify();
+ }
+
@Test
void testExecute_withEscapePatternConfigured_schemaTableAndColumnNamesEscaped() throws Exception
{
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test to ensure columns not present in CompositeTable metadata still throw NoSuchColumnException
To fully pin down the behaviour and prevent regressions, please also add a test asserting that `getValue` still throws `NoSuchColumnException` when the requested column is absent from the composite metadata itself. This will confirm the new `isMissingFromPart` logic doesn’t mask exceptions for truly unknown columns.
Suggested implementation:
```java
assertThat(renamed.getRowCount()).as("row count preserved.").isEqualTo(1);
assertThat(renamed.getValue(0, "VAL")).as("row data preserved.").isEqualTo("hello");
}
@Test
void testGetValue_whenColumnMissingFromCompositeMetaData_throwsNoSuchColumnException()
throws Exception
{
final Column[] columns = new Column[] {new Column("COL1", DataType.INTEGER)};
final DefaultTable table = new DefaultTable("TABLE_1", columns);
table.addRow(new Object[] {1});
final CompositeTable composite =
new CompositeTable(table.getTableMetaData(), new ITable[] {table});
assertThatThrownBy(() -> composite.getValue(0, "UNKNOWN_COL"))
.isInstanceOf(NoSuchColumnException.class)
.hasMessageContaining("UNKNOWN_COL");
}
// -------------------------------------------------------------------------
// getValue(int, String) across parts with divergent columns (issue #708)
// -------------------------------------------------------------------------
```
To compile:
1. Ensure `assertThatThrownBy` is statically imported (e.g. `import static org.assertj.core.api.Assertions.assertThatThrownBy;`) if not already present.
2. Confirm `CompositeTable`, `ITable`, and `NoSuchColumnException` imports are present in this test class; if not, add:
- `import org.dbunit.dataset.CompositeTable;`
- `import org.dbunit.dataset.ITable;`
- `import org.dbunit.dataset.NoSuchColumnException;`
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8d6a2cbea6
ℹ️ 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".
…alue fixup! fix(dataset): Fix NoSuchColumnException in CompositeTable.getValue Address sourcery-ai review feedback on PR #908: * CompositeTable.getValue() validates columnName against this table's own metadata once via the O(1) cached AbstractTableMetaData#getColumnIndex, instead of rescanning _metaData.getColumns() with Columns.getColumn on every call; only one linear scan (over the smaller part-table column list) remains, addressing the flagged hot-path concern. * Add CompositeTableTest coverage asserting a column absent from the composite's own metadata still throws NoSuchColumnException, pinning down that isMissingFromPart cannot mask a genuinely unknown column. * Make InsertOperationIT's new test order-insensitive when reading rows back without an ORDER BY, matching by COLUMN0 instead of assuming a particular physical row order across database vendors. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pe1saB9qxPumJjvBLLGFKj
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Addressed both overall comments in 3d99ac3 (fixup, will squash into 8d6a2cb before merge):
Also added the suggested unknown-column regression test (replied inline). Full unit suite (1945 tests) and the |
3d99ac3 to
7781c02
Compare
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…alue fixup! fix(dataset): Fix NoSuchColumnException in CompositeTable.getValue Address chatgpt-codex-connector review feedback on PR #908 (P1 correctness issue): the fix belonged in InsertOperation, not CompositeTable. CompositeTable.getValue() is shared by every DatabaseOperation, not just InsertOperation. UpdateOperation and DeleteOperation never override getIgnoreMapping/equalsIgnoreMapping (they use AbstractBatchOperation's no-op defaults), so they bind every requested column's value directly -- including primary-key columns in a WHERE clause. Making CompositeTable itself resolve a missing column to ITable.NO_VALUE meant a CompositeTable part missing a primary-key column would silently bind SQL NULL into a DELETE/UPDATE WHERE clause instead of throwing, causing a silent no-op instead of a clear error. RefreshOperation's own update/exists-check paths have the same direct-bind exposure; only its insert-new-row path reuses InsertOperation's methods and so correctly keeps the new leniency. * Revert CompositeTable.java to its pre-#708 state -- back to unconditional delegation, no missing-column handling. This also resolves a separate flagged concern about ColumnFilterTable-wrapped parts, since CompositeTable no longer makes any assumption about a part's own declared columns at all. * Move the fix into InsertOperation.getIgnoreMapping/equalsIgnoreMapping via a new getValueOrNoValueIfMissing helper, so only INSERT's already-existing ignore-mapping mechanism (which omits the column from the generated statement entirely) can ever substitute NO_VALUE for a missing column. * Replace the two CompositeTableTest additions with one confirming CompositeTable.getValue() still throws for this exact scenario -- documenting that the leniency intentionally does not live there. * Add a DeleteOperationTest regression test proving a CompositeTable part missing a primary-key column still throws NoSuchColumnException rather than silently deleting zero rows. * Update the changes.xml entry to describe the corrected fix location and scoping rationale. InsertOperationTest and InsertOperationIT needed no changes: the externally observable INSERT behavior (SQL generated, rows inserted) is identical under the corrected implementation. Full unit suite (1945 tests) and the hsqldb-2-7 Insert/Delete/Update/Refresh operation ITs (35 tests) all green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pe1saB9qxPumJjvBLLGFKj
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Codex's P1 comment was right and caught a real correctness bug in the previous round — thanks for the sharp review. Summary of what changed in 52c5f6a (fixup, will squash before merge): The fix moved from
Full unit suite (1945 tests) and the |
CompositeDataSet merges same-named tables from separate datasets (e.g. two flat-XML files both inserting into the same table) into a single CompositeTable that exposes only the first part's metadata. Reading a column that a later part never itself declared threw NoSuchColumnException instead of being treated as not supplied, surfacing through InsertOperation's core insert path via equalsIgnoreMapping/getIgnoreMapping. * CompositeTable.getValue() now resolves such a column to ITable.NO_VALUE for that part's rows - the same sentinel InsertOperation already uses to omit a column from a generated statement - instead of letting the part's own NoSuchColumnException propagate. * Add CompositeTableTest, InsertOperationTest, and InsertOperationIT regression coverage; all three reproduce the original stack trace when the fix is reverted. Refs: 708 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pe1saB9qxPumJjvBLLGFKj
52c5f6a to
484c026
Compare
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
CompositeDataSet merges same-named tables from separate datasets (e.g. two flat-XML files both inserting into the same table) into a single CompositeTable that exposes only the first part's metadata. Reading a column that a later part never itself declared threw NoSuchColumnException instead of being treated as not supplied, surfacing through InsertOperation's core insert path via equalsIgnoreMapping/getIgnoreMapping.
Refs: 708
Claude-Session: https://claude.ai/code/session_01Pe1saB9qxPumJjvBLLGFKj
Summary by Sourcery
Handle optional columns consistently when CompositeDataSet merges tables with divergent schemas so InsertOperation can omit missing columns instead of failing with NoSuchColumnException.
Bug Fixes:
Documentation:
Tests:
Summary by CodeRabbit
Bug Fixes
NULLinstead of causing an error.Documentation