Home > Back-end >  Multiple queries to Postgres within the same function
Multiple queries to Postgres within the same function

Time:07-16

I'm new to Go, so sorry for the silly question in advance!

I'm using Gin framework and want to make multiple queries to the database within the same handler (database/sql lib/pq)

userIds := []int{}
bookIds := []int{}
var id int

/* Handling first query here */
rows, err := pgClient.Query(getUserIdsQuery)
defer rows.Close()
if err != nil {
    return
}
for rows.Next() {
    err := rows.Scan(&id)
    if err != nil {
        return
    }
    userIds = append(userIds, id)
}

/* Handling second query here */
rows, err = pgClient.Query(getBookIdsQuery)
defer rows.Close()
if err != nil {
    return
}
for rows.Next() {
    err := rows.Scan(&id)
    if err != nil {
        return
    }
    bookIds = append(bookIds, id)
}

I have a couple of questions regarding this code (any improvements and best practices would be appreciated)

  1. Does Go properly handle defer rows.Close() in such a case? I mean I have reassignment of rows variable later down the code, so will compiler track both and properly close at the end of a function?

  2. Is it ok to reuse id shared var or should I redeclare it while iterating within rows.Next() loop?

  3. What's the better approach of having even more queries within one handler? Should I have some kind of Writer that accepts query and slice and populate it with ids retrieved?

Thanks.

CodePudding user response:

I've never worked with go-pg library, and my answer is mostly focused on the other stuff, which are generic, and are not specific to golang or go-pg.

    1. Regardless of the fact that the rows here has the same reference while being shared between 2 queries (so one rows.Close() call would suffice, unless the library has some special implementation), defining two variables is cleaner, like userRows and bookRows.
    1. Although I already said that I have not worked with go-pg, I believe that you wont need to iterate through rows and scan the id for all the rows manually, I believe that the lib has provided some API like this (based on the quick look on the documentations):
    userIds := []int{}
    err := pgClient.Query(&userIds, "select id from users where ...", args...)
    
    1. Regarding your second question, it depends on what you mean by "ok". Since your doing some synchronous iteration, I don't think it would result into bugs, but when it comes to coding style, personally, I wouldn't do this.
    1. I think that the best thing to do in your case is this:
    // repo layer
    func getUserIds(args whatever) ([]int, err) {...}
    // these can be exposed, based on your packaging logic
    func getBookIds(args whatever) ([]int, err) {...}
    
    // service layer, or wherever you want to aggregate both queries
    func getUserAndBookIds() ([]int, []int, err) {
        userIds, err := getUserIds(...)
        // potential error handling
        bookIds, err := getBookIds(...)
        // potential error handling
        return userIds, bookIds, nil // you have done err handling earlier
    }
    
    I think this code is easier to read/maintain. You won't face the variable reassignment and other issues.

You can take a look at the go-pg documentations for more details on how to improve your query.

  • Related