postgresqlgosqlc

Getting Empty Records Back Using Golang, SQLC and PostgreSQL


I am trying to implement pagination in my GO backend API. The API uses SQLC for the abstraction through to my PostgreSQL database. The issue is, It returns blank records and i cannot figure out what am doing wrong. The following are relevant codes pertaining to my current work:

  1. SQLC code:
const getAllFeeds = `-- name: GetAllFeeds :many
SELECT count(*) OVER(), id, created_at, updated_at, name, url, user_id, version
FROM feeds
WHERE ($1 = '' OR to_tsvector('simple', name) @@ plainto_tsquery('simple', $1))
AND ($2 = '' OR url LIKE '%' || $2 || '%')
ORDER BY id ASC
LIMIT $3 OFFSET $4
`

type GetAllFeedsParams struct {
    Column1 interface{}
    Column2 interface{}
    Limit   int32
    Offset  int32
}

type GetAllFeedsRow struct {
    Count     int64
    ID        uuid.UUID
    CreatedAt time.Time
    UpdatedAt time.Time
    Name      string
    Url       string
    UserID    int64
    Version   int32
}

func (q *Queries) GetAllFeeds(ctx context.Context, arg GetAllFeedsParams) ([]GetAllFeedsRow, error) {
    rows, err := q.db.QueryContext(ctx, getAllFeeds,
        arg.Column1,
        arg.Column2,
        arg.Limit,
        arg.Offset,
    )
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    var items []GetAllFeedsRow
    for rows.Next() {
        var i GetAllFeedsRow
        if err := rows.Scan(
            &i.Count,
            &i.ID,
            &i.CreatedAt,
            &i.UpdatedAt,
            &i.Name,
            &i.Url,
            &i.UserID,
            &i.Version,
        ); err != nil {
            return nil, err
        }
        items = append(items, i)
    }
    if err := rows.Close(); err != nil {
        return nil, err
    }
    if err := rows.Err(); err != nil {
        return nil, err
    }
    return items, nil
}
  1. Golang code:
func (m FeedModel) GetAllFeeds(name string, url string, filters Filters) ([]*Feed, Metadata, error) {
    // create our timeout context. All of them will just be 5 seconds
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    // retrieve our data
    fmt.Printf("Executing GetAllFeeds with name: [%s], url: [%s], limit: %d, offset: %d\n",
        name, url, filters.limit(), filters.offset())

    rows, err := m.DB.GetAllFeeds(ctx, database.GetAllFeedsParams{
        Column1: name,
        Column2: sql.NullString{String: url, Valid: url != ""}, // Convert string to sql.NullString
        Limit:   int32(filters.limit()),
        Offset:  int32(filters.offset()),
    })
    //check for an error
    if err != nil {
        return nil, Metadata{}, err
    }
    fmt.Println("Rows: ", rows)
    totalRecords := 0
    feeds := []*Feed{}
    for _, row := range rows {
        var feed Feed
        totalRecords = int(row.Count)
        feed.ID = row.ID
        feed.CreatedAt = row.CreatedAt
        feed.UpdatedAt = row.UpdatedAt
        feed.Name = row.Name
        feed.Url = row.Url
        feed.Version = row.Version
        feed.UserID = row.UserID
        feeds = append(feeds, &feed)
    }
    // Generate a Metadata struct, passing in the total record count and pagination
    // parameters from the client.
    metadata := calculateMetadata(totalRecords, filters.Page, filters.PageSize)
    return feeds, metadata, nil
}

Sample output: Executing GetAllFeeds with name: [], url: [], limit: 30, offset: 0 Rows: [] Total Records: 0 Initial schema:

-- +goose Up
CREATE TABLE feeds(
    id UUID PRIMARY KEY,
        created_at timestamp(0) with time zone NOT NULL DEFAULT NOW(),
        updated_at timestamp(0) with time zone NOT NULL DEFAULT NOW(),
        name TEXT NOT NULL,
        url TEXT UNIQUE NOT NULL,
        version INT NOT NULL DEFAULT 1,
        user_id bigserial NOT NULL REFERENCES users(id) ON DELETE CASCADE
);

If I run the following directly in psql or even pgAdmin i get All records:

SELECT count(*) OVER(), id, created_at, updated_at, name, url, user_id, version
                FROM feeds
                WHERE (to_tsvector('simple', name) @@ plainto_tsquery('simple','' ) OR  ''= '')
                AND (url LIKE '%' || '' || '%' OR '' = '')
                ORDER BY id ASC
                LIMIT 30 OFFSET 0;

Now my question is, is there anything I may be missing since am getting empty results back using my own Golang code?. Just to add, I have my own functions that read the queries from the URL, so am sure on what is read. The offset and limit defaults to 0 and 30 if none is provided so the API returns all records.

Thank you in Advance.


Solution

  • As per the comments your where clause includes:

    AND ($2 = '' OR url LIKE '%' || $2 || '%')
    

    $2 is GetAllFeedsParams.Column2 in the go code and, if url is an empty string (which it is in the example), you set GetAllFeedsParams.Column2.Valid to false. This means that the parameter is null. The upshot of this is that the where clause is effectively:

    AND (null = '' OR url LIKE '%' || null || '%')
    

    This can be simplified to:

    AND (null OR null)
    

    The final result being null (which is not true so the row is rejected).