From 01f261c71ca3cd4f360e569c1a7bf83a24dcf814 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Fri, 11 Jul 2014 08:16:21 -0500 Subject: [PATCH] Conform closer to database/sql style and add no rows test --- conn.go | 23 +++++++-------------- conn_test.go | 37 +++++++++++++++++++++++++++++++++- examples/url_shortener/main.go | 8 +++++--- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/conn.go b/conn.go index e33fba8d..8f08508e 100644 --- a/conn.go +++ b/conn.go @@ -85,15 +85,9 @@ func (ct CommandTag) RowsAffected() int64 { return n } -// NotSingleRowError is returned when exactly 1 row is expected, but 0 or more than -// 1 row is returned -type NotSingleRowError struct { - RowCount int64 -} - -func (e NotSingleRowError) Error() string { - return fmt.Sprintf("Expected to find 1 row exactly, instead found %d", e.RowCount) -} +var ErrNoRows = errors.New("no rows in result set") +var ErrNotificationTimeout = errors.New("notification timeout") +var ErrDeadConn = errors.New("conn is dead") type ProtocolError string @@ -101,9 +95,6 @@ func (e ProtocolError) Error() string { 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. // config.Host must be specified. config.User will default to the OS user name. // 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. -// 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) { if len(c.notifications) > 0 { notification := c.notifications[0] @@ -370,7 +361,7 @@ func (c *Conn) WaitForNotification(timeout time.Duration) (*Notification, error) if err != nil { 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() { - return nil, NotificationTimeoutError + return nil, ErrNotificationTimeout } return nil, err } @@ -417,7 +408,7 @@ func (r *Row) Scan(dest ...interface{}) (err error) { if !qr.NextRow() { if qr.Err() == nil { - return errors.New("No rows") + return ErrNoRows } else { 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) { if !c.alive { - return 0, nil, DeadConnError + return 0, nil, ErrDeadConn } t, err = c.mr.rxMsg() diff --git a/conn_test.go b/conn_test.go index f94dfed8..f9abbaa6 100644 --- a/conn_test.go +++ b/conn_test.go @@ -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) { t.Parallel() @@ -788,7 +802,7 @@ func TestListenNotify(t *testing.T) { // when timeout occurs 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) } 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) { t.Parallel() diff --git a/examples/url_shortener/main.go b/examples/url_shortener/main.go index d2a1a20b..f6a22c37 100644 --- a/examples/url_shortener/main.go +++ b/examples/url_shortener/main.go @@ -45,11 +45,13 @@ func afterConnect(conn *pgx.Conn) (err error) { func getUrlHandler(w http.ResponseWriter, req *http.Request) { 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) - } else if _, ok := err.(pgx.NotSingleRowError); ok { + case pgx.ErrNoRows: http.NotFound(w, req) - } else { + default: http.Error(w, "Internal server error", http.StatusInternalServerError) } }