Do not double call termContext in QueryEx

QueryEx was calling termContext and rows.fatal on err of sendPreparedQuery.
rows.fatal calls rows.Close which already calls termContext. This sequence of
calls was causing underlying io timeout errors to be returned instead of context
errors.

In addition, added fatalWriteErr helper method to allow recovery of write
timeout errors where no bytes were written.

This should solve flickering errors on Travis.
batch-wip
Jack Christensen 2017-05-20 10:58:44 -05:00
parent b8c043780d
commit 2df4b1406b
2 changed files with 19 additions and 7 deletions

19
conn.go
View File

@ -795,9 +795,11 @@ func (c *Conn) prepareEx(name, sql string, opts *PrepareExOptions) (ps *Prepared
buf = append(buf, 'S') buf = append(buf, 'S')
buf = pgio.AppendInt32(buf, 4) buf = pgio.AppendInt32(buf, 4)
_, err = c.conn.Write(buf) n, err := c.conn.Write(buf)
if err != nil { if err != nil {
if fatalWriteErr(n, err) {
c.die(err) c.die(err)
}
return nil, err return nil, err
} }
c.readyForQuery = false c.readyForQuery = false
@ -1085,8 +1087,8 @@ func (c *Conn) sendPreparedQuery(ps *PreparedStatement, arguments ...interface{}
buf = append(buf, 'S') buf = append(buf, 'S')
buf = pgio.AppendInt32(buf, 4) buf = pgio.AppendInt32(buf, 4)
_, err = c.conn.Write(buf) n, err := c.conn.Write(buf)
if err != nil { if err != nil && fatalWriteErr(n, err) {
c.die(err) c.die(err)
} }
c.readyForQuery = false c.readyForQuery = false
@ -1094,6 +1096,17 @@ func (c *Conn) sendPreparedQuery(ps *PreparedStatement, arguments ...interface{}
return err return err
} }
// fatalWriteError takes the response of a net.Conn.Write and determines if it is fatal
func fatalWriteErr(bytesWritten int, err error) bool {
// Partial writes break the connection
if bytesWritten > 0 {
return true
}
netErr, is := err.(net.Error)
return !(is && netErr.Timeout())
}
// Exec executes sql. sql can be either a prepared statement name or an SQL string. // Exec executes sql. sql can be either a prepared statement name or an SQL string.
// arguments should be referenced positionally from the sql string as $1, $2, etc. // arguments should be referenced positionally from the sql string as $1, $2, etc.
func (c *Conn) Exec(sql string, arguments ...interface{}) (commandTag CommandTag, err error) { func (c *Conn) Exec(sql string, arguments ...interface{}) (commandTag CommandTag, err error) {

View File

@ -398,16 +398,15 @@ func (c *Conn) QueryEx(ctx context.Context, sql string, options *QueryExOptions,
err = c.initContext(ctx) err = c.initContext(ctx)
if err != nil { if err != nil {
rows.fatal(err) rows.fatal(err)
return rows, err return rows, rows.err
} }
err = c.sendPreparedQuery(ps, args...) err = c.sendPreparedQuery(ps, args...)
if err != nil { if err != nil {
rows.fatal(err) rows.fatal(err)
err = c.termContext(err)
} }
return rows, err return rows, rows.err
} }
func (c *Conn) sanitizeAndSendSimpleQuery(sql string, args ...interface{}) (err error) { func (c *Conn) sanitizeAndSendSimpleQuery(sql string, args ...interface{}) (err error) {