Cookies management by TermsFeed Cookie Consent

Implement reading methods

12/21

Now, we are going to implement the reading methods: All() and GetByName(). Let’s add them to the repository_postgresql_classic.go file.

All() method

The All() method returns all rows from the websites table. It uses the DB.QueryContext() method to get the rows for a SQL SELECT query. It returns them in the form of an sql.Rows object, which represents a cursor to the DB rows. It is important to remember that after you finish working with the sql.Rows object, it should be closed, which is what the line below does (defer keyword means that the rows.Close() will be called at the end of the All() method):

defer rows.Close()

Using the Rows.Next() method in the loop, which returns true if there are any more rows in the sql.Rows object, and the Rows.Scan(), which copies the successive row values to the given variables, we can convert the raw data from the database into a slice of Website objects. Note that in case of any error in retrieving or copying the data, the whole procedure is interrupted, and an error is returned. If successful, the All() method returns a slice of Website objects.

GetByName() method

The GetByName() function retrieves one Website record with the name given as an argument. It works similarly to All(), except that instead of using the DB.QueryContext() method, it uses DB.QueryRowContext(), which returns at most one row in the form of sql.Row, and there is no need to close it. To copy the raw values to the Website object, the same Scan() function is used as before, except that in case of an error, we check if it is an instance of sql.ErrNoRows indicating no results. If such an error appears, we map it to our predefined repository error ErrNotExist. So if a user makes a mistake and, as an argument, he passes a website name that is not in the database, he will receive a clear error indicating the reason.

website/repository_postgresql_classic.go

// ...

func (r *PostgreSQLClassicRepository) All(ctx context.Context) ([]Website, error) {
    rows, err := r.db.QueryContext(ctx, "SELECT * FROM websites")
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var all []Website
    for rows.Next() {
        var website Website
        if err := rows.Scan(&website.ID, &website.Name, &website.URL, &website.Rank); err != nil {
            return nil, err
        }
        all = append(all, website)
    }
    return all, nil
}

func (r *PostgreSQLClassicRepository) GetByName(ctx context.Context, name string) (*Website, error) {
    row := r.db.QueryRowContext(ctx, "SELECT * FROM websites WHERE name = $1", name)

    var website Website
    if err := row.Scan(&website.ID, &website.Name, &website.URL, &website.Rank); err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return nil, ErrNotExist
        }
        return nil, err
    }
    return &website, nil
}
12/21