@@ -2644,8 +2644,14 @@ void Cleanup()
26442644 _certificates = null ;
26452645 }
26462646
2647+ [ MemberNotNull ( nameof ( _resetWithoutDeallocateMessage ) ) ]
26472648 void GenerateResetMessage ( )
26482649 {
2650+ // Generate a reset message that resets connection state without using DISCARD ALL.
2651+ // This is used in two scenarios:
2652+ // 1. When closing a pooled connection that has prepared statements (DISCARD ALL would deallocate them)
2653+ // 2. When closing a connection within an enlisted System.Transactions transaction (DISCARD ALL cannot
2654+ // run inside a transaction block, but its component commands can)
26492655 var sb = new StringBuilder ( "SET SESSION AUTHORIZATION DEFAULT;RESET ALL;" ) ;
26502656 _resetWithoutDeallocateResponseCount = 2 ;
26512657 if ( DatabaseInfo . SupportsCloseAll )
@@ -2753,6 +2759,35 @@ internal async Task Reset(bool async)
27532759 }
27542760 }
27552761
2762+ /// <summary>
2763+ /// Called when a pooled connection with an enlisted System.Transactions transaction is closed.
2764+ /// Since we're inside a transaction block, we cannot send DISCARD ALL;
2765+ /// we prepend a reset message that only includes commands that can safely run within a transaction.
2766+ /// </summary>
2767+ internal void ResetWithinEnlistedTransaction ( )
2768+ {
2769+ // We start user action in case a keeplive happens concurrently, or a concurrent user command (bug)
2770+ using var _ = StartUserAction ( attemptPgCancellation : false ) ;
2771+
2772+ // Our buffer may contain unsent prepended messages, so clear it out.
2773+ WriteBuffer . Clear ( ) ;
2774+ PendingPrependedResponses = 0 ;
2775+
2776+ ResetReadBuffer ( ) ;
2777+
2778+ if ( _sendResetOnClose )
2779+ {
2780+ if ( _resetWithoutDeallocateMessage is null )
2781+ {
2782+ GenerateResetMessage ( ) ;
2783+ }
2784+
2785+ PrependInternalMessage ( _resetWithoutDeallocateMessage , _resetWithoutDeallocateResponseCount ) ;
2786+ }
2787+
2788+ DataReader . UnbindIfNecessary ( ) ;
2789+ }
2790+
27562791 /// <summary>
27572792 /// The connector may have allocated an oversize read buffer, to hold big rows in non-sequential reading.
27582793 /// This switches us back to the original one and returns the buffer to <see cref="ArrayPool{T}" />.
0 commit comments