Conform closer to database/sql style and add no rows test

scan-io
Jack Christensen 2014-07-11 08:16:21 -05:00
parent 566d713285
commit 01f261c71c
3 changed files with 48 additions and 20 deletions

23
conn.go
View File

@ -85,15 +85,9 @@ func (ct CommandTag) RowsAffected() int64 {
return n return n
} }
// NotSingleRowError is returned when exactly 1 row is expected, but 0 or more than var ErrNoRows = errors.New("no rows in result set")
// 1 row is returned var ErrNotificationTimeout = errors.New("notification timeout")
type NotSingleRowError struct { var ErrDeadConn = errors.New("conn is dead")
RowCount int64
}
func (e NotSingleRowError) Error() string {
return fmt.Sprintf("Expected to find 1 row exactly, instead found %d", e.RowCount)
}
type ProtocolError string type ProtocolError string
@ -101,9 +95,6 @@ func (e ProtocolError) Error() string {
return string(e) return string(e)
} }
var NotificationTimeoutError = errors.New("Notification Timeout")
var DeadConnError = errors.New("Connection is dead")
// Connect establishes a connection with a PostgreSQL server using config. // Connect establishes a connection with a PostgreSQL server using config.
// config.Host must be specified. config.User will default to the OS user name. // config.Host must be specified. config.User will default to the OS user name.
// Other config fields are optional. // Other config fields are optional.
@ -340,7 +331,7 @@ func (c *Conn) Listen(channel string) (err error) {
} }
// WaitForNotification waits for a PostgreSQL notification for up to timeout. // WaitForNotification waits for a PostgreSQL notification for up to timeout.
// If the timeout occurs it returns pgx.NotificationTimeoutError // If the timeout occurs it returns pgx.ErrNotificationTimeout
func (c *Conn) WaitForNotification(timeout time.Duration) (*Notification, error) { func (c *Conn) WaitForNotification(timeout time.Duration) (*Notification, error) {
if len(c.notifications) > 0 { if len(c.notifications) > 0 {
notification := c.notifications[0] notification := c.notifications[0]
@ -370,7 +361,7 @@ func (c *Conn) WaitForNotification(timeout time.Duration) (*Notification, error)
if err != nil { if err != nil {
c.conn.SetReadDeadline(zeroTime) // we can only return one error and we already have one -- so ignore possiple error from SetReadDeadline c.conn.SetReadDeadline(zeroTime) // we can only return one error and we already have one -- so ignore possiple error from SetReadDeadline
if err, ok := err.(*net.OpError); ok && err.Timeout() { if err, ok := err.(*net.OpError); ok && err.Timeout() {
return nil, NotificationTimeoutError return nil, ErrNotificationTimeout
} }
return nil, err return nil, err
} }
@ -417,7 +408,7 @@ func (r *Row) Scan(dest ...interface{}) (err error) {
if !qr.NextRow() { if !qr.NextRow() {
if qr.Err() == nil { if qr.Err() == nil {
return errors.New("No rows") return ErrNoRows
} else { } else {
return qr.Err() return qr.Err()
} }
@ -940,7 +931,7 @@ func (c *Conn) processContextFreeMsg(t byte, r *MsgReader) (err error) {
func (c *Conn) rxMsg() (t byte, r *MsgReader, err error) { func (c *Conn) rxMsg() (t byte, r *MsgReader, err error) {
if !c.alive { if !c.alive {
return 0, nil, DeadConnError return 0, nil, ErrDeadConn
} }
t, err = c.mr.rxMsg() t, err = c.mr.rxMsg()

View File

@ -182,6 +182,20 @@ func TestConnectWithMD5Password(t *testing.T) {
} }
} }
func TestConnectWithConnectionRefused(t *testing.T) {
t.Parallel()
// Presumably nothing is listening on 127.0.0.1:1
bad := *defaultConnConfig
bad.Host = "127.0.0.1"
bad.Port = 1
_, err := pgx.Connect(bad)
if !strings.Contains(err.Error(), "connection refused") {
t.Fatal("Unable to establish connection: " + err.Error())
}
}
func TestParseURI(t *testing.T) { func TestParseURI(t *testing.T) {
t.Parallel() t.Parallel()
@ -788,7 +802,7 @@ func TestListenNotify(t *testing.T) {
// when timeout occurs // when timeout occurs
notification, err = listener.WaitForNotification(time.Millisecond) notification, err = listener.WaitForNotification(time.Millisecond)
if err != pgx.NotificationTimeoutError { if err != pgx.ErrNotificationTimeout {
t.Errorf("WaitForNotification returned the wrong kind of error: %v", err) t.Errorf("WaitForNotification returned the wrong kind of error: %v", err)
} }
if notification != nil { if notification != nil {
@ -1066,6 +1080,27 @@ func TestQueryRowPreparedErrors(t *testing.T) {
} }
} }
func TestQueryRowNoResults(t *testing.T) {
t.Parallel()
conn := mustConnect(t, *defaultConnConfig)
defer closeConn(t, conn)
sql := "select 1 where 1=0"
psName := "selectNothing"
mustPrepare(t, conn, psName, sql)
for _, sql := range []string{sql, psName} {
var n int32
err := conn.QueryRow(sql).Scan(&n)
if err != pgx.ErrNoRows {
t.Errorf("Expected pgx.ErrNoRows, got %v", err)
}
ensureConnValid(t, conn)
}
}
func TestQueryPreparedEncodeError(t *testing.T) { func TestQueryPreparedEncodeError(t *testing.T) {
t.Parallel() t.Parallel()

View File

@ -45,11 +45,13 @@ func afterConnect(conn *pgx.Conn) (err error) {
func getUrlHandler(w http.ResponseWriter, req *http.Request) { func getUrlHandler(w http.ResponseWriter, req *http.Request) {
var url string var url string
if err := pool.QueryRow("getUrl", req.URL.Path).Scan(&url); err == nil { err := pool.QueryRow("getUrl", req.URL.Path).Scan(&url)
switch err {
case nil:
http.Redirect(w, req, url, http.StatusSeeOther) http.Redirect(w, req, url, http.StatusSeeOther)
} else if _, ok := err.(pgx.NotSingleRowError); ok { case pgx.ErrNoRows:
http.NotFound(w, req) http.NotFound(w, req)
} else { default:
http.Error(w, "Internal server error", http.StatusInternalServerError) http.Error(w, "Internal server error", http.StatusInternalServerError)
} }
} }