How to pass variable ids to statement.Query() in golang? - go

I have this query in postgres which queries 1 or n users based on the parameters passed:
select name, phone from clients where id in ('id1','id2')
Now when I try to use this at golang I'm having problems approaching how to pass this type of variable arguments to the statement.Query() function:
ids := []string{"0aa6c0c5-e44e-4187-b128-6ae4b2258df0", "606b0182-269f-469a-bb29-26da4fa0302b"}
rows, err := stmt.Query(ids...)
This throws error: Cannot use 'ids' (type []string) as type []interface{}
When I check in source code query it can receive many variables of type interface:
func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
return s.QueryContext(context.Background(), args...)
}
If I do this manually it works:
rows, err := stmt.Query("0aa6c0c5-e44e-4187-b128-6ae4b2258df0", "606b0182-269f-469a-bb29-26da4fa0302b")
But of course I need the args to be 1 or many more, and dynamically generated.
I'm using Sqlx lib.

As we can see on the Query() method scheme and also from the error message, the method requires an argument in []interface{} type.
func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
return s.QueryContext(context.Background(), args...)
}
In your code, the ids variable hold []string data. Change it to []interface{} so it'll meet Query() requirements, then it'll work.
ids := []interface{}{
"0aa6c0c5-e44e-4187-b128-6ae4b2258df0",
"606b0182-269f-469a-bb29-26da4fa0302b",
}
rows, err := stmt.Query(ids...)

Related

go-redis Eval func return value type, when Lua script returns an array

When a Lua script returns a table array during an Eval call, how can it be converted to a []string in go?
The redis cli returns bulk replies in the following format.
1) val1
2) val2
Will go-redis eval function return the bulk entries as
["val1", "val2"]
Redis returns Lua table arrays as RESP2 arrays. The Go client then will map that response to Go native types. The relevant documentation for go-redis is found here: Lua and Go types.
The tl;dr is that Lua tables are indeed mapped to a bulk reply, and the Go client maps that to a slice of interface: []interface{}.
Both go-redis script Run and Eval return a *Cmd. You can use methods on this type to retrieve the output as Go types. Result gives (interface{}, error), which you can type-assert to whatever you want, otherwise, StringSlice is a convenience getter to retrieve []string right away.
So it looks like:
script := redis.NewScript(`
local foo = {"val1", "val2"}
return foo
`)
cmd := script.Run(/* parameters */)
i, err := cmd.Result() // (interface, error)
// or
ss, err := cmd.StringSlice() // ([]string, error)
If the values aren't actually all strings, use Slice to get the []interface{} slice, and then inspect the elements individually.
You can use the encoding/json package to convert a JSON string to a slice of strings.
package main
import (
"encoding/json"
"fmt"
)
// jsonString is the JSON string that you want to convert to a slice of strings.
const jsonString = `["value1", "value2"]`
func main() {
var stringSlice []string
// Unmarshal the JSON string into the stringSlice variable.
err := json.Unmarshal([]byte(jsonString), &stringSlice)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(stringSlice) // ["value1", "value2"]
}

Generic Go code to retrieve multiple rows from BigQuery

I am writing some utils to retrieve multiple rows from BigQuery in a generic way using Go.
e.g.
type User struct {name string, surname string}
type Car struct {model string, platenumber string}
query1:="SELECT name, surname FROM UserTable"
query2:="SELECT model, platenumber FROM CarTable"
cars, _ := query2.GetResults()
users, _ := query1.GetResults()
OR
cars := []Car{}
query2.GetResults(cars) // and it would append to the slice
I am unsure about the signature of GetResults. I need somehow to pass the type to BigQuery library so it can retrieve the data and map it to the struct correctly. But at the same time I need to make it generic so it can be used for different types.
At the moment my GetResults looks like this: it doesn't work, the error is:
bigquery: cannot convert *interface {} to ValueLoader (need pointer to []Value, map[string]Value, or struct)[]
But I cannot pass directly the struct as I want to make it generic.
func (s *Query) GetResults() ([]interface{}, error) {
var result []interface{}
job, err := s.Run()
if err != nil {
s.log.Error(err, "error in running the query")
return nil, err
}
it, err := job.ReadData()
if err != nil {
s.log.Error(err, "error in reading the data")
return nil, err
}
var row interface{}
for {
err := it.Next(&row)
if err != nil {
fmt.Print(err)
break
}
result = append(result, row)
}
return result, nil
}
Is there another way to achieve that? Or is the good way not to create a method like that?
I've tried quite a lot of different things, with or without pointer, with or without array, by modifying the args, or returning a new list, nothing seem to work, and doing all of that feels a bit wrong regarding the nature "easy" of what I am trying to achieve.
I've also looked into doing the following
GetResults[T any]() ([]T, error)
But it's "excluded" as GetResults is part of an interface (and we can't define generic for a method of an interface). And I can't/don't want to define a type for all the interface, as it impacts other interfaces.

How do you pass a slice of *interface{} as arguments?

I want to use Scan() in package sql, but the number of columns, and hence the number of arguments, will change at runtime. This is the signature of Scan():
func (rs *Rows) Scan(dest ...interface{}) error
According to the documentation, *interface{} is one of the types accepted by Scan(). So I want to create a slice of []*interface{} and that expand as arguments.
This is what I thought would work:
func query(database *sql.DB) {
rows, _ := database.Query("select * from testTable")
for rows.Next() {
data := make([]*interface{}, 2)
err := rows.Scan(data...) // Compilation error
fmt.Printf("%v%v\n", *data[0], *data[1])
if err != nil {
fmt.Println(err.Error())
}
}
}
Compilation fails with cannot use data (type []*interface {}) as type []interface {} in argument to rows.Scan. I thought that data... would expand to &data[0], &data[1], but apparently not. I don't understand the error message. *interface{} is compatible with interface{}, so why can't I expand the slice of pointers to interface types?
This works:
func query(database *sql.DB) {
rows, _ := database.Query("select * from testTable")
for rows.Next() {
data := make([]*interface{}, 2)
err := rows.Scan(&data[0], &data[1]) // Only changed this line
fmt.Printf("%v%v\n", *data[0], *data[1]) // Outputs "[48][116 101 120 116]"
if err != nil {
fmt.Println(err.Error())
}
}
}
I can't use this however, because the number of columns is unknown at compile time. How can I write this code so that I can pass a variable number of *interface{} to rows.Scan()?
First, you must not use []*interface{} slice of pointers to interface rather than []interface{} where the interfaces are pointers. []*interface{} is different from []interface{}. Just create a slice of interfaces where each element is a pointer to a concrete type.
Here is a snippet how you would do this.
var x int
var s string
data := []interface{}{&x, &s}
rows.Scan(data...)
Note on the use of the ... spread operator.
Here are some related questions that will explain a bit more:
golang: slice of struct != slice of interface it implements?
Cannot convert []string to []interface {}
If you really want to pass a []*interface{} (perhaps you don't know the concrete types of the output) you must first wrap each *interface{} in a interface{}:
values := make([]interface{}, columnsCount)
for i := range values {
values[i] = new(interface{})
}
Individual values passed into a ...interface{} parameter are automatically wrapped in a interface{}, but just like []int... won't satisfy ...interface{}, neither will []*interface{}....

Get queue length of redis golang

I am using go-redis package. I fired below query:
rd.LLen("queue_1")
Which returning me below result with type *redis.IntCmd:
llen queue_1: 100001
Count is perfect but I want only count with type of int. Can anyone help?
the LLen func looks like: func (c *cmdable) LLen(key string) *IntCmd
So it returns an IntCmd type. IntCmd provides this function: func (cmd *IntCmd) Val() int64
So you can do e.g.
cmd := rd.LLen("queue_1")
i := cmd.Val() //i will be an int64 type
The documentation for IntCmd shows the following:
func (cmd *IntCmd) Result() (int64, error)
func (cmd *IntCmd) Val() int64
So it seems you can either get the int64 value and any error to check, or just the int64 value. Is there a reason this is not sufficient?
You can call them as follows:
// Get the value and check the error.
if val, err := res.Result(); err != nil {
panic(err)
} else {
fmt.Println("Queue length: ", val)
}
// Get the value, but ignore errors (probably a bad idea).
fmt.Println("Queue length: ", res.Val())
Use the Result or Val method. As documented, these return the int64 value returned from the command.

Golang pass nil as optional argument to a function?

In Golang, http.NewRequest has a specification like this:
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)
However, I can pass nil as the body option if I don't want to pass the body to an io.Reader object, like this:
req, err := http.NewRequest("GET", "http://www.blahblah.org", nil)
How do I implement this functionality in my code? I have a function that I want to pass an optional string value so that it can page through API results however if I pass a nil to the string input I get this:
./snippets.go:32: cannot convert nil to type string
The parameters for my function look like this:
func getChallenges(after string) ([]challenge, string, error)
Go does not have "optional" arguments as a generally understood concept in other languages; nil is just the zero value for an interface (io.Reader in this case).
The equivalent zero value for a string is an empty string:
getChallenges("")
If you want to accept 0 or more of the same argument type, you use the variadic syntax:
func getChallenges(after ...string) ([]challenge, string, error)
You can modify you function to receive pointer value, like this:
func getChallenges(after *string) ([]challenge, string, error)
Then you can pass nil as an argument to it. But don't forget to check after for nil value inside your function before dereferencing it, or you will get a nil pointer exception:
func getChallenges(after *string) ([]challenge, string, error) {
if after == nil {
// No value specified
} else {
fmt.Printf("After: %s\n", *after) // Note pointer dereferencing with "*"
}
// ...
}
Another option:
Just use two functions:
func getChallenges(after string) {}
func getAllChallenges() {
return getChallenges(/* some default value here */)
}
you can use ellipse operator to send the optional parameters.. don't pass anything in optional parameter and check the length of parameter.
it should solve your problem
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
Maybe wrap it in a struct?
type NilableString struct {
value string;
}
You can use reflect.
In fact io.Reader is a interface.
So you can define signature like func getChallenges(after interface{}) ([]challenge, string, error)
interface{} is a empty interface, that is interface for anything.
But I suggest you use syntax args... to pass slice , refer to fmt.Printf implementation for usage, because if you pass no string the slice len is 0 and
this will avoid the reflect which I think too heavy for your function.

Resources