Search code examples
postgresqlgointerfaceabstraction

Abstracting PSQL Write Query


I have a typical function that takes a post request from the frontend and decodes the data into a struct in order to put it into a psql database. You can see the code below. My problem is that I want to be able to abstract this function so that I can give it any amount of variables of any type so that for every request I don't have to have a separate write handler.

It looks difficult because I would have to somehow pass in a way to abstract var profitReq profitReq to work for any struct. If golang had some sort of eval string method I would know how to do this, but someone correct me if I'm wrong, but I don't think that it does.

The other place I need to change is in QueryRow - I have to be able to pass it in a variable number of variables. I could construct the string easily enough, but I'm not sure how to append variables to that QueryRow necessarily. If I append all the variables to an array for example, I can't pass that array into QueryRow as that's not how it's structured. Again, here some sort of eval statement would help.

I'm new to golang, but I've seen a lot of cool things related to interfaces, which I admittedly don't understand very well. Would there be a way to use an interface here that would help?

Thanks to anyone who can help!

func Write_profit_table(profitWriteChannel chan string, profitType string, req *http.Request) {
    var profitReq profitReq;
    err := json.NewDecoder(req.Body).Decode(&profitReq);
    if err!=nil{
        log.Panic(err)
    }

    NotInDB, _ := Search_userinfo_table(profitReq.Email)

    if NotInDB == false {
        var lastInsertId int
        err2 := db.QueryRow("INSERT INTO profit(email, type, dateArray, amount, interest, compounded, recurring, name, description, profitFrom) 
                VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) returning uid;", 
                profitReq.Email, profitReq.Type, pq.Array(profitReq.DateArray), profitReq.Profit, profitReq.Interest, 
                profitReq.Compounded, profitReq.Recurring, profitReq.Name, profitReq.Description, profitReq.ProfitFrom).Scan(&lastInsertId);
        if err2!=nil{
            log.Panic(err2)
        }
    }

    profitWriteChannel<-"finished writing to profit"
}

Solution

  • The feature you're looking for is called generics.
    Generics are not supported by Go 1.x
    Luckily for us, there's a proposal for them for Go 2 (called Contracts)

    What can you do until then?

    1. Duplicate your code (probably what you're doing now)
    2. Use interfaces
      If you know that this method will always query by email, you could create simple interface for that purpose: type Emailer interface { Email() string }

    3. Use empty interface (interface{}) and reflection to figure out what columns you have.

    4. Write your own generator. A bit like (1), but you don't have to do this yourself.

    Here's the gist of it: https://play.golang.org/p/A_2YKWLvmn-