Conversation
368cc98 to
c9212f3
Compare
c9212f3 to
a22a718
Compare
5b91717 to
cf122e5
Compare
aab2833 to
e950992
Compare
5a8a530 to
86af6d4
Compare
86af6d4 to
078b193
Compare
There was a problem hiding this comment.
Pull request overview
This PR simplifies field reads in NpgsqlDataReader by removing Stream-specific special-casing and moving Stream handling into the ADO type info resolver/converter pipeline, with additional reader state tweaks to support the new flow and improve inlining/hot-path performance.
Changes:
- Replace
Streamspecial-casing inNpgsqlDataReader.GetFieldValue*with resolver-based Stream conversion (StreamByteaConverter). - Introduce
PgReader.StreamCanSeekand propagate sequential/non-sequential seekability decisions intoPgReader.GetStream(). - Cleanup and small refactors: unify
-1null sentinel usage, tighten cleanup/reset behavior, and rename nested-read revert API.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Npgsql/ThrowHelper.cs | Changes ThrowIndexOutOfRangeException formatting overload to take an int argument. |
| src/Npgsql/Replication/PgOutput/ReplicationValue.cs | Refactors replication value reads to use a shared generic Get<T>(async, ...) path; exposes sync GetStream/GetTextReader via blocking. |
| src/Npgsql/NpgsqlDataReader.cs | Removes Stream fast path from GetFieldValue*, introduces DbNullSentinel, threads DataFormat through GetInfo, sets stream seekability via PgReader.StreamCanSeek, and resets reader/buffer references on cleanup. |
| src/Npgsql/Internal/ResolverFactories/AdoTypeInfoResolverFactory.cs | Adds fallback Stream type info resolution and enables Stream converter text-format support for relevant mappings. |
| src/Npgsql/Internal/PgReader.cs | Adds StreamCanSeek, replaces GetStream(canSeek, ...) with GetStream() using the new flag, and renames nested-scope revert method. |
| src/Npgsql/Internal/Converters/Primitive/TextConverters.cs | Adjusts ConsumeChars end-of-stream condition logic. |
| src/Npgsql/Internal/Converters/Primitive/ByteaConverters.cs | Implements StreamByteaConverter as an actual Stream reader via PgReader.GetStream(), with format support controlled by supportsTextFormat. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
8365094 to
dd543b8
Compare
dd543b8 to
eadb2f1
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/Npgsql/Replication/PgOutput/ReplicationValue.cs:169
ThrowIfInitialized()only checksPgReader.Initialized, butConsume()sets_isConsumed = trueafter committing (which clearsPgReader.Initialized). AfterConsume()has been called,Get<T>()can be invoked again and will re-Initand read from the wrong position. Consider also checking_isConsumed(or setting a separate flag) and throwing the same "Column has already been consumed" exception when true.
void ThrowIfInitialized()
{
if (PgReader.Initialized)
throw new InvalidOperationException("Column has already been consumed");
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
src/Npgsql/Replication/PgOutput/ReplicationValue.cs:169
ThrowIfInitialized()only checksPgReader.Initialized, butConsume(...)sets_isConsumed = trueafter committing the reader (soInitializedbecomes false). That allowsGet*methods to be called again after consumption, potentially reading from an already-consumed buffer position. Add a guard for_isConsumed(or reintroduce the previous active-state check) and ensure the exception message matches the condition.
void ThrowIfInitialized()
{
if (PgReader.Initialized)
throw new InvalidOperationException("Column has already been consumed");
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
34efa01 to
dcbfdd5
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
fe41bb8 to
5a9300d
Compare
5a9300d to
6e1cf1e
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
# Conflicts: # src/Npgsql/NpgsqlDataReader.cs
6e1cf1e to
6d4e08b
Compare
| if ((uint)ordinal > (uint)ColumnCount) | ||
| ThrowHelper.ThrowIndexOutOfRangeException("Ordinal must be between 0 and " + (ColumnCount - 1)); | ||
| if ((uint)ordinal >= (uint)ColumnCount) | ||
| ThrowHelper.ThrowIndexOutOfRangeException("Ordinal is out of range, value must be between 0 and {0} (exclusive).", ColumnCount); |
There was a problem hiding this comment.
This guard condition change was a copilot suggestion, as we were dropping through to the array indexer bounds check for the last column. To do so the message needed a rework as well (otherwise you'd read e.g. "must be between 0 and 0" for a one column row).
| get | ||
| { | ||
| if ((uint)ordinal < (uint)Count) | ||
| if ((uint)ordinal >= (uint)Count) |
There was a problem hiding this comment.
This guard condition change was a copilot suggestion, as we were dropping through to the array indexer bounds check for the last column. To do so the message needed a rework as well (otherwise you'd read e.g. "must be between 0 and 0" for a one column row).
This moves the stream handling into the Ado resolver.
Based on #5476
Any reviews should start from the last commit
Simplify by resolving Stream by default as a converterAfter this pack of changes (though that ideally includes #5471) we are able to fully inline everything on the hot path into GetFieldValueCore.
DPGO was even happy to make one huge 8000 bytes method (arm64) out of my local reproduction of the fortunes scenario, inlining even GetFieldValueCore itself.
Imprecise local benchmarking shows about a 3% improvement in fortunes RPS.