fix(postgresql): Fix PostgreSQLOidDataType failing to read an oid tha… - #935
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Reviewer's GuideAdjust PostgreSQLOidDataType#getSqlValue() to treat non–large-object oids as nulls while preserving real error propagation and transaction usability, and add tests/docs/changelog entries to cover the new behavior and regression scenarios (issue 693). Sequence diagram for PostgreSQLOidDataType#getSqlValue handling non_large_object oidssequenceDiagram
actor Caller
participant PostgreSQLOidDataType
participant Connection
participant LargeObjectManager as LOBManager
participant LargeObject
Caller->>PostgreSQLOidDataType: getSqlValue(column, resultSet)
PostgreSQLOidDataType->>Connection: getAutoCommit()
PostgreSQLOidDataType->>Connection: setAutoCommit(false)
PostgreSQLOidDataType->>Connection: unwrap(PGConnection)
PostgreSQLOidDataType->>LOBManager: getLargeObjectAPI()
PostgreSQLOidDataType->>PostgreSQLOidDataType: readLargeObject(connection, lobj, oid)
activate PostgreSQLOidDataType
PostgreSQLOidDataType->>Connection: setSavepoint()
PostgreSQLOidDataType->>LOBManager: open(oid, LargeObjectManager.READ)
alt [large object exists]
LOBManager-->>PostgreSQLOidDataType: LargeObject
PostgreSQLOidDataType->>LargeObject: size()
PostgreSQLOidDataType->>LargeObject: read(buf, 0, size)
PostgreSQLOidDataType->>LargeObject: close()
PostgreSQLOidDataType-->>Caller: byte[]
else [SQLException]
LOBManager-->>PostgreSQLOidDataType: SQLException
PostgreSQLOidDataType->>Connection: rollback(savepoint)
alt [SQLState 42704]
PostgreSQLOidDataType-->>Caller: null
else [other SQLState]
PostgreSQLOidDataType-->>Caller: SQLException (propagated)
end
end
deactivate PostgreSQLOidDataType
PostgreSQLOidDataType->>Connection: setAutoCommit(autoCommit)
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Warning Review limit reached
Next review available in: 22 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 (2)
📝 WalkthroughWalkthroughPostgreSQL OID reads now return ChangesPostgreSQL OID handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant getSqlValue
participant readLargeObject
participant PostgreSQLConnection
participant LargeObject
getSqlValue->>readLargeObject: read OID
readLargeObject->>PostgreSQLConnection: create savepoint
readLargeObject->>LargeObject: open OID
LargeObject-->>readLargeObject: bytes or SQL exception
readLargeObject->>PostgreSQLConnection: rollback failed open
readLargeObject-->>getSqlValue: bytes, null, or propagated exception
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 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
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="src/test/java/org/dbunit/ext/postgresql/PostgreSQLOidDataTypeTest.java" line_range="82-86" />
<code_context>
+
+ private final PostgreSQLOidDataType type = new PostgreSQLOidDataType();
+
+ private void mockConnectionChain() throws SQLException
+ {
+ when(resultSet.getStatement()).thenReturn(statement);
+ when(statement.getConnection()).thenReturn(connection);
+ when(connection.getAutoCommit()).thenReturn(true);
+ when(connection.unwrap(PGConnection.class)).thenReturn(pgConnection);
+ when(pgConnection.getLargeObjectAPI()).thenReturn(largeObjectManager);
</code_context>
<issue_to_address>
**suggestion (testing):** Add coverage for connections that already have auto-commit disabled
Right now all tests stub `connection.getAutoCommit()` as `true`, so they only cover the path where we disable and then restore auto-commit. Please also add a test where `getAutoCommit()` initially returns `false` to verify we don’t accidentally enable auto-commit and that savepoint/rollback still behave correctly. You could parameterize `mockConnectionChain` or add a separate helper for the `false` case.
Suggested implementation:
```java
private final PostgreSQLOidDataType type = new PostgreSQLOidDataType();
private void mockConnectionChain() throws SQLException
{
mockConnectionChain(true);
}
private void mockConnectionChain(boolean autoCommit) throws SQLException
{
when(resultSet.getStatement()).thenReturn(statement);
when(statement.getConnection()).thenReturn(connection);
when(connection.getAutoCommit()).thenReturn(autoCommit);
when(connection.unwrap(PGConnection.class)).thenReturn(pgConnection);
when(pgConnection.getLargeObjectAPI()).thenReturn(largeObjectManager);
}
```
```java
void testGetSqlType_onNewInstance_returnsTypesBigint()
{
assertThat(type.getSqlType())
.as("getSqlType() should return Types.BIGINT for OID type.")
.isEqualTo(Types.BIGINT);
}
@Test
void testSetSqlValue_whenAutoCommitDisabled_doesNotEnableAutoCommitAndUsesSavepoint() throws Exception
{
mockConnectionChain(false);
long oid = 123L;
when(largeObjectManager.createLO()).thenReturn(oid);
when(largeObjectManager.open(eq(oid), anyInt())).thenReturn(largeObject);
type.setSqlValue(oid, statement, 1);
verify(connection, never()).setAutoCommit(true);
verify(connection, never()).setAutoCommit(false);
verify(connection).setSavepoint();
verify(connection).rollback(any(Savepoint.class));
}
@Test
```
1. Ensure the following static imports exist (or equivalent qualified calls are used) in the test file:
- `import static org.mockito.ArgumentMatchers.any;`
- `import static org.mockito.ArgumentMatchers.anyInt;`
- `import static org.mockito.ArgumentMatchers.eq;`
- `import static org.mockito.Mockito.never;`
- `import static org.mockito.Mockito.verify;`
2. If the production implementation does not use a savepoint/rollback when auto-commit is disabled, adjust the `verify(connection).setSavepoint();` and `verify(connection).rollback(any(Savepoint.class));` expectations to match the actual behavior (or remove them if not applicable).
3. The new test assumes `type.setSqlValue(Object, PreparedStatement, int)` is the method under test and that it uses `LargeObjectManager` and `Savepoint`; if the method signature or behavior differ, update the test accordingly.
</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.
Actionable comments posted: 2
🤖 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 `@src/main/java/org/dbunit/ext/postgresql/PostgreSQLOidDataType.java`:
- Around line 64-87: Complete the JavaDoc in PostgreSQLOidDataType by adding
documentation for the public getSqlValue method, including complete descriptions
of its parameters, return value, and declared exceptions as applicable. Update
the readLargeObject JavaDoc so each `@param` and `@return` description begins with a
capital letter and ends with a period, while preserving the existing behavior
and meaning.
- Around line 91-105: Limit SQLState 42704-to-null handling to the lobj.open
call in the PostgreSQLOidDataType read flow. Handle open failures separately,
ensure the LargeObject is closed during cleanup after a successful open, and
rethrow exceptions from size, read, or close instead of masking them. Add tests
covering open, size/read, and close failure paths.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: da8f5db4-bab0-4fde-a6a5-7aa39ae4d13c
📒 Files selected for processing (5)
src/changes/changes.xmlsrc/main/java/org/dbunit/ext/postgresql/PostgreSQLOidDataType.javasrc/site/asciidoc/databases/postgresql.adocsrc/test/java/org/dbunit/ext/postgresql/PostgreSQLOidDataTypeTest.javasrc/test/java/org/dbunit/ext/postgresql/PostgresSQLOidIT.java
A PostgreSQL oid column is a generic object identifier, not necessarily a large object reference (e.g. a table's own oid via 'table'::regclass). PostgreSQLOidDataType#getSqlValue() previously had a commented-out swallow-everything attempt for this, explicitly noted as wrong since it would hide real errors - so it just let the SQLException propagate and fail the whole read instead. * Distinguish "not a large object" from every other failure via the driver's own SQLState (42704/undefined_object, exposed as org.postgresql.util.PSQLState.UNDEFINED_OBJECT), but only for the LargeObjectManager#open() call itself: a failure reading, sizing, or closing an object that did open is a real error and always propagates, since the oid is already proven to be a large object by that point. * Open the large object under a savepoint: PostgreSQL aborts the entire enclosing transaction on any failed command, so rolling back to the savepoint on failure clears the abort without discarding any other work already done in that transaction, keeping the connection usable for the rest of the read. A large object that did open is closed on the success path only; recovering via the savepoint rollback already reclaims it server-side, and attempting to close it after a failed read would itself throw since the transaction is still aborted at that point. * Add Mockito-based PostgreSQLOidDataTypeTest coverage: zero-oid, ambient auto-commit already disabled, a genuine large object, a 42704 open failure, a non-42704 open failure, and post-open size/read and close failures. * Extend PostgresSQLOidIT with a live-container case inserting two distinct real catalog oids via raw SQL (dbUnit's own setSqlValue() always creates a genuine large object, so it can't produce this scenario itself); reading the second row also proves the connection recovers, since advancing past row 1 requires a real resultSet.next() round-trip. * Document the new behavior in postgresql.adoc, including a Known Quirks note that a non-large-object oid reads identically to a genuine SQL NULL. Refs: 693 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XBjSJ2bgVtwqyxGv2nvwP9
aff2570 to
b1927c7
Compare
…t isn't a large object
A PostgreSQL oid column is a generic object identifier, not necessarily a large object reference (e.g. a table's own oid via 'table'::regclass, the issue's own example). PostgreSQLOidDataType#getSqlValue() previously had a commented-out swallow-everything attempt for this, explicitly noted as wrong since it would hide real errors - so it just let the SQLException propagate and fail the whole read instead.
Refs: 693
Claude-Session: https://claude.ai/code/session_01XBjSJ2bgVtwqyxGv2nvwP9
Summary by Sourcery
Handle PostgreSQL OID values that do not reference large objects without failing the entire read, and ensure transactions recover cleanly after large-object access errors.
Bug Fixes:
Enhancements:
Documentation:
Tests:
Chores:
Summary by CodeRabbit
Bug Fixes
oidhandling for missing or invalid large-object references.Documentation
oidread/write behavior, error handling, recovery, and knownNULLvalue limitations.