Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to execute an IN lookup in SQL using Golang?

Tags:

sql

go

What does Go want for the second param in this SQL query. I am trying to use the IN lookup in postgres.

stmt, err := db.Prepare("SELECT * FROM awesome_table WHERE id= $1 AND other_field IN $2")
rows, err := stmt.Query(10, ???)

What I really want:

SELECT * FROM awesome_table WHERE id=10 AND other_field IN (this, that);
like image 558
a.m. Avatar asked Nov 28 '13 16:11

a.m.


People also ask

What is the use of SQL in Golang?

Golang’s sql package comes from Golang’s standard library, and it provides an interface around SQL databases. To use an SQL database, the we must use the sql package with the driver for the SQL database. We’ll see how this is done when we use the go-mssqldb database driver with the sql package.

How to reduce the risk of SQL injection in Golang?

Ensure you use a data validation library to validate user input before using it in your SQL query. This reduces the risk of an SQL injection attack against the connected database. Examples of data validation libraries for Golang include go-ozzo and govalidator.

How to persist data in a Golang application?

This means that data that needs persisting should be stored in an external backing service such as a database. When applying this principle to your Golang written applications, you might need to use an SQL database to persist data.

Is it possible to execute a go query without passing query arguments?

As an alternative, you could pre-build the SQL query and execute without passing query arguments, for an example see Go and IN clause in Postgres. This user is first on the weekly Go Language leaderboard. Thanks for this clear answer ! It's already working but I'm gonna check prebuild queries.


6 Answers

It looks like you may be using the pq driver. pq recently added Postgres-specific Array support via pq.Array (see pull request 466). You can get what you want via:

stmt, err := db.Prepare("SELECT * FROM awesome_table WHERE id= $1 AND other_field = ANY($2)")
rows, err := stmt.Query(10, pq.Array([]string{'this','that'})

I think this generates the SQL:

SELECT * FROM awesome_table WHERE id=10 AND other_field = ANY('{"this", "that"}');

Note this utilizes prepared statements, so the inputs should be sanitized.

like image 147
Pete Avatar answered Nov 02 '22 23:11

Pete


Query just takes varargs to replace the params in your sql so, in your example, you would just do

rows, err := stmt.Query(10)

say, this and that of your second example were dynamic, then you'd do

stmt, err := db.Prepare("SELECT * FROM awesome_table WHERE id=$1 AND other_field IN ($2, $3)")
rows, err := stmt.Query(10,"this","that")

If you have variable args for the "IN" part, you can do (play)

package main

import "fmt"
import "strings"

func main() {
    stuff := []interface{}{"this", "that", "otherthing"}
    sql := "select * from foo where id=? and name in (?" + strings.Repeat(",?", len(stuff)-1) + ")"
    fmt.Println("SQL:", sql)
    args := []interface{}{10}
    args = append(args, stuff...)
    fakeExec(args...)
    // This also works, but I think it's harder for folks to read
    //fakeExec(append([]interface{}{10},stuff...)...)
}

func fakeExec(args ...interface{}) {
    fmt.Println("Got:", args)
}
like image 30
David Budworth Avatar answered Nov 02 '22 23:11

David Budworth


Incase anyone like me was trying to use an array with a query, here is an easy solution.

get https://github.com/jmoiron/sqlx

ids := []int{1, 2, 3}
q,args,err := sqlx.In("SELECT id,username FROM users WHERE id IN(?);", ids) //creates the query string and arguments
//you should check for errors of course
q = sqlx.Rebind(sqlx.DOLLAR,q) //only if postgres
rows, err := db.Query(q,args...) //use normal POSTGRES/ANY SQL driver important to include the '...' after the Slice(array)
like image 43
Krtko Avatar answered Nov 02 '22 22:11

Krtko


With PostgreSQL, at least, you have the option of passing the entire array as a string, using a single placeholder:

db.Query("select 1 = any($1::integer[])", "{1,2,3}")

That way, you can use a single query string, and all the string concatenation is confined to the parameter. And if the parameter is malformed, you don't get an SQL injection; you just get something like: ERROR: invalid input syntax for integer: "xyz"

https://groups.google.com/d/msg/golang-nuts/vHbg09g7s2I/RKU7XsO25SIJ

like image 43
Ivan Rave Avatar answered Nov 03 '22 00:11

Ivan Rave


if you use sqlx, you can follow this way: https://github.com/jmoiron/sqlx/issues/346

arr := []string{"this", "that"}
query, args, err := sqlx.In("SELECT * FROM awesome_table WHERE id=10 AND other_field IN (?)", arr)
 
query = db.Rebind(query) // sqlx.In returns queries with the `?` bindvar, rebind it here for matching the database in used (e.g. postgre, oracle etc, can skip it if you use mysql)
rows, err := db.Query(query, args...)
like image 21
yihao ye Avatar answered Nov 02 '22 22:11

yihao ye


var awesome AwesomeStruct
var awesomes []*AwesomeStruct

ids := []int{1,2,3,4}
q, args, err := sqlx.In(`
  SELECT * FROM awesome_table WHERE id=(?) AND other_field IN (?)`, 10, ids)

// use .Select for multiple return
err = db.Select(&awesomes, db.SQL.Rebind(q), args...)

// use .Get for single return
err = db.Get(&awesome, db.SQL.Rebind(q), args...)
like image 33
7urkm3n Avatar answered Nov 02 '22 23:11

7urkm3n