@@ -4,12 +4,16 @@ import (
44 "context"
55 "errors"
66 "fmt"
7+ "io"
8+ "net"
79 "os"
810 "testing"
911 "time"
1012
1113 "github.com/jackc/pgx/v5"
14+ "github.com/jackc/pgx/v5/internal/faultyconn"
1215 "github.com/jackc/pgx/v5/pgconn"
16+ "github.com/jackc/pgx/v5/pgproto3"
1317 "github.com/jackc/pgx/v5/pgxtest"
1418 "github.com/stretchr/testify/assert"
1519 "github.com/stretchr/testify/require"
@@ -1060,6 +1064,71 @@ func TestSendBatchStatementTimeout(t *testing.T) {
10601064
10611065}
10621066
1067+ func TestSendBatchHandlesTimeoutBetweenParseAndDescribe (t * testing.T ) {
1068+ // Not parallel because it is a timing sensitive test.
1069+
1070+ ctx , cancel := context .WithTimeout (context .Background (), 120 * time .Second )
1071+ defer cancel ()
1072+
1073+ var faultyConn * faultyconn.Conn
1074+ faultyConnTestRunner := pgxtest .DefaultConnTestRunner ()
1075+ faultyConnTestRunner .CreateConfig = func (ctx context.Context , t testing.TB ) * pgx.ConnConfig {
1076+ config , err := pgx .ParseConfig (os .Getenv ("PGX_TEST_DATABASE" ))
1077+ require .NoError (t , err )
1078+ config .AfterNetConnect = func (ctx context.Context , config * pgconn.Config , conn net.Conn ) (net.Conn , error ) {
1079+ faultyConn = faultyconn .New (conn )
1080+ return faultyConn , nil
1081+ }
1082+ return config
1083+ }
1084+
1085+ // Only need to test modes that use Parse/Describe.
1086+ extendedQueryModes := []pgx.QueryExecMode {
1087+ pgx .QueryExecModeCacheStatement ,
1088+ pgx .QueryExecModeCacheDescribe ,
1089+ pgx .QueryExecModeDescribeExec ,
1090+ pgx .QueryExecModeExec ,
1091+ }
1092+
1093+ pgxtest .RunWithQueryExecModes (ctx , t , faultyConnTestRunner , extendedQueryModes , func (ctx context.Context , t testing.TB , conn * pgx.Conn ) {
1094+ _ , err := conn .Exec (ctx , "set statement_timeout = '100ms'" )
1095+ require .NoError (t , err )
1096+
1097+ batch := & pgx.Batch {}
1098+ batch .Queue ("select 1" )
1099+ batch .Queue ("select 2" )
1100+
1101+ faultyConn .HandleFrontendMessage = func (backendWriter io.Writer , msg pgproto3.FrontendMessage ) error {
1102+ if _ , ok := msg .(* pgproto3.Describe ); ok {
1103+ time .Sleep (200 * time .Millisecond )
1104+ }
1105+ buf , err := msg .Encode (nil )
1106+ if err != nil {
1107+ return err
1108+ }
1109+ _ , err = backendWriter .Write (buf )
1110+ return err
1111+ }
1112+
1113+ err = conn .SendBatch (ctx , batch ).Close ()
1114+ require .Error (t , err )
1115+ var pgErr * pgconn.PgError
1116+ require .True (t , errors .As (err , & pgErr ))
1117+ require .Equal (t , "57014" , pgErr .Code )
1118+
1119+ faultyConn .HandleFrontendMessage = nil
1120+
1121+ _ , err = conn .Exec (ctx , "set statement_timeout = default" )
1122+ require .NoError (t , err )
1123+
1124+ batch = & pgx.Batch {}
1125+ batch .Queue ("select 1" )
1126+ batch .Queue ("select 2" )
1127+ err = conn .SendBatch (ctx , batch ).Close ()
1128+ require .NoError (t , err )
1129+ })
1130+ }
1131+
10631132func ExampleConn_SendBatch () {
10641133 ctx , cancel := context .WithTimeout (context .Background (), 120 * time .Second )
10651134 defer cancel ()
0 commit comments