11package wire
22
33import (
4+ "bytes"
45 "context"
56 "errors"
67 "fmt"
@@ -56,6 +57,7 @@ type Session struct {
5657 Statements StatementCache
5758 Portals PortalCache
5859 Attributes map [string ]interface {}
60+ reader * buffer.Reader
5961
6062 // pipelining
6163 ParallelPipeline ParallelPipelineConfig
@@ -93,6 +95,7 @@ func isExtendedQueryMessage(t types.ClientMessage) bool {
9395// This method keeps consuming messages until the client issues a close message
9496// or the connection is terminated.
9597func (srv * Session ) consumeCommands (ctx context.Context , conn net.Conn , reader * buffer.Reader , writer * buffer.Writer ) error {
98+ srv .reader = reader
9699 srv .logger .Debug ("ready for query... starting to consume commands" )
97100
98101 err := readyForQuery (writer , types .ServerIdle )
@@ -326,7 +329,13 @@ func (srv *Session) handleSimpleQuery(ctx context.Context, reader *buffer.Reader
326329 return srv .WriteError (writer , err )
327330 }
328331
329- err = statements [index ].fn (ctx , NewDataWriter (ctx , srv , statements [index ].columns , nil , NoLimit , reader , writer ), nil )
332+ portal := & Portal {
333+ statement : & Statement {
334+ fn : statements [index ].fn ,
335+ columns : statements [index ].columns ,
336+ },
337+ }
338+ err = portal .execute (ctx , NoLimit , reader , writer )
330339 if err != nil {
331340 return srv .WriteError (writer , err )
332341 }
@@ -753,7 +762,11 @@ func (srv *Session) handleClose(ctx context.Context, reader *buffer.Reader, writ
753762 return writer .End ()
754763}
755764
756- // executePipelined handles Execute in parallel pipeline mode
765+ // executePipelined handles Execute in parallel pipeline mode. When the
766+ // Execute carries a row limit (or the portal already has a suspended
767+ // iterator) it is queued as a synchronous event that runs inline during
768+ // drain, because it needs the portal's iter.Pull2 state. Otherwise it
769+ // launches a goroutine for parallel execution.
757770func (srv * Session ) executePipelined (ctx context.Context , writer * buffer.Writer , name string , limit uint32 ) error {
758771 portal , err := srv .Portals .Get (ctx , name )
759772 if err != nil {
@@ -764,42 +777,46 @@ func (srv *Session) executePipelined(ctx context.Context, writer *buffer.Writer,
764777 return srv .drainQueueAndWriteError (ctx , writer , errors .New ("unknown portal" ))
765778 }
766779
767- // Create result channel and queue the event
768- resultChan := make (chan * QueuedDataWriter , 1 )
769- srv .ResponseQueue .Enqueue (NewExecuteEvent (resultChan , portal .formats ))
780+ resultChan := make (chan * executeResult , 1 )
781+ srv .ResponseQueue .Enqueue (NewExecuteEvent (resultChan ))
770782
771- // Launch async execution
772- go srv .executeAsync (ctx , portal , limit , resultChan )
783+ prev := portal .pending
784+ done := make (chan struct {})
785+ portal .pending = done
786+ go srv .executeAsync (ctx , done , portal , Limit (limit ), prev , resultChan )
773787
774788 return nil
775789}
776790
777- // executeAsync runs the portal execution in a separate goroutine
778- func (srv * Session ) executeAsync (ctx context.Context , portal * Portal , limit uint32 , resultChan chan <- * QueuedDataWriter ) {
791+ // executeAsync runs portal.execute in a separate goroutine, capturing the
792+ // wire output into a buffer. If prev is non-nil, it waits for the previous
793+ // goroutine on the same portal to finish before starting.
794+ func (srv * Session ) executeAsync (ctx context.Context , done chan struct {}, portal * Portal , limit Limit , prev chan struct {}, resultChan chan <- * executeResult ) {
795+ defer close (done )
779796 defer close (resultChan )
780797 defer func () {
781798 if r := recover (); r != nil {
782- collector := NewQueuedDataWriter (ctx , portal .statement .columns , Limit (limit ))
783- collector .SetError (fmt .Errorf ("panic during execution: %v" , r ))
784- resultChan <- collector
799+ resultChan <- & executeResult {err : fmt .Errorf ("panic during execution: %v" , r )}
785800 }
786801 }()
787802
788- srv .logger .Debug ("starting async execution" ,
789- slog .Bool ("has_function" , portal .statement .fn != nil ))
803+ if prev != nil {
804+ <- prev
805+ }
790806
791- collector := NewQueuedDataWriter ( ctx , portal . statement . columns , Limit ( limit ) )
807+ srv . logger . Debug ( "starting async execution" )
792808
793- err := portal .statement .fn (ctx , collector , portal .parameters )
794- if err != nil {
795- collector .SetError (err )
796- }
809+ buf := & bytes.Buffer {}
810+ w := buffer .NewWriter (srv .logger , buf )
811+
812+ err := portal .execute (ctx , limit , srv .reader , w )
813+
814+ result := & executeResult {buf : buf , err : err }
797815
798816 srv .logger .Debug ("async execution complete" ,
799- slog .Bool ("has_error" , collector .GetError () != nil ),
800- slog .Int ("rows" , int (collector .Written ())))
817+ slog .Bool ("has_error" , err != nil ))
801818
802- resultChan <- collector
819+ resultChan <- result
803820}
804821
805822// handleSync handles the Sync message (extended query protocol)
@@ -924,22 +941,16 @@ func (srv *Session) writeQueuedResponse(ctx context.Context, writer *buffer.Writ
924941 return srv .writeColumnDescription (ctx , writer , event .Formats , event .Columns )
925942
926943 case ResponseExecute :
927- // Execute writes DataRows followed by CommandComplete
928944 if event .Result == nil {
929- // No result yet, this shouldn't happen in normal flow
930945 return errors .New ("execute event has no result" )
931946 }
932947
933- // Check for execution error
934- if err := event .Result .GetError (); err != nil {
935- return srv .WriteError (writer , err )
948+ if event .Result .err != nil {
949+ return srv .WriteError (writer , event .Result .err )
936950 }
937951
938- // Use DataWriter for correct encoding
939- // Note: We use NoLimit here because the result is already limited during execution
940- dataWriter := NewDataWriter (ctx , srv , event .Result .Columns (), event .Formats , NoLimit , nil , writer )
941-
942- return event .Result .Replay (ctx , dataWriter )
952+ _ , err := writer .Write (event .Result .buf .Bytes ())
953+ return err
943954
944955 default :
945956 return fmt .Errorf ("unknown response event kind: %v" , event .Kind )
0 commit comments