If I have a table that returns something like:
id: 1
names: {Jim, Bob, Sam}
names is a varchar array.
How do I scan that back into a []string in Go?
I'm using lib/pg
Right now I have something like
rows, err := models.Db.Query("SELECT pKey, names FROM foo")
for rows.Next() {
var pKey int
var names []string
err = rows.Scan(&pKey, &names)
}
I keep getting:
panic: sql: Scan error on column index 1: unsupported Scan, storing driver.Value type []uint8 into type *[]string
It looks like I need to use StringArray
https://godoc.org/github.com/lib/pq#StringArray
But, I think I'm too new to Go to understand exactly how to use:
func (a *StringArray) Scan(src interface{})
You are right, you can use StringArray but you don't need to call the
func (a *StringArray) Scan(src interface{})
method yourself, this will be called automatically by rows.Scan when you pass it anything that implements the Scanner interface.
So what you need to do is to convert your []string to *StringArray and pass that to rows.Scan, like so:
rows, err := models.Db.Query("SELECT pKey, names FROM foo")
for rows.Next() {
var pKey int
var names []string
err = rows.Scan(&pKey, (*pq.StringArray)(&names))
}
Long Story Short, use like this to convert pgSQL array to GO array, here 5th column is coming as a array :
var _temp3 []string
for rows.Next() {
// ScanRows scan a row into temp_tbl
err := rows.Scan(&_temp, &_temp0, &_temp1, &_temp2, pq.Array(&_temp3))
In detail :
To insert a row that contains an array value, use the pq.Array function like this:
// "ins" is the SQL insert statement
ins := "INSERT INTO posts (title, tags) VALUES ($1, $2)"
// "tags" is the list of tags, as a string slice
tags := []string{"go", "goroutines", "queues"}
// the pq.Array function is the secret sauce
_, err = db.Exec(ins, "Job Queues in Go", pq.Array(tags))
To read a Postgres array value into a Go slice, use:
func getTags(db *sql.DB, title string) (tags []string) {
// the select query, returning 1 column of array type
sel := "SELECT tags FROM posts WHERE title=$1"
// wrap the output parameter in pq.Array for receiving into it
if err := db.QueryRow(sel, title).Scan(pq.Array(&tags)); err != nil {
log.Fatal(err)
}
return
}
Note: that in lib/pq, only slices of certain Go types may be passed to pq.Array().
Another example in which varchar array in pgSQL in generated at runtime in 5th column, like :
--> predefined_allow false admin iam.create {secrets,configMap}
I converted this as,
Q := "SELECT ar.policy_name, ar.allow, ar.role_name, pro.operation_name, ARRAY_AGG(pro.resource_id) as resources FROM iam.authorization_rules ar LEFT JOIN iam.policy_rules_by_operation pro ON pro.id = ar.operation_id GROUP BY ar.policy_name, ar.allow, ar.role_name, pro.operation_name;"
tx := g.db.Raw(Q)
rows, _ := tx.Rows()
defer rows.Close()
var _temp string
var _temp0 bool
var _temp1 string
var _temp2 string
var _temp3 []string
for rows.Next() {
// ScanRows scan a row into temp_tbl
err := rows.Scan(&_temp, &_temp0, &_temp1, &_temp2, pq.Array(&_temp3))
if err != nil {
return nil, err
}
fmt.Println("Query Executed...........\n", _temp, _temp0, _temp1, _temp2, _temp3)
}
Output :
Query Executed...........
predefined_allow false admin iam.create [secrets configMap]
Related
I am using Golang to insert data into a DB. basically my query looks like below
var cols = "(col1, col2, col3)"
var values = "($1, $2, $3)"
var query = fmt.Sprintf("INSERT INTO %s %s VALUES %s", myTable, cols, values)
res, err := db.Exec(query, thing.val1, thing.val2, thing.val3)
The only things available from res are lastInsertId and # of rows affected. But what I need is the rows affected. The reason being is that I insert data into a psql database which has an AUTOINCREMENT id column - so I want the data back with that.
For example - with Java hibernate I can do what this answer explains. I don't have to re-query the DB for the ID.
EDIT: I tried to use the lastInsertId method and got this error
LastInsertId is not supported by this driver
Assuming you just want the auto-incremented value(s) in a column called id and this is an insert with the pq driver
var cols = "(col1, col2, col3)"
var values = "($1, $2, $3)"
var query = fmt.Sprintf(
"INSERT INTO %s %s VALUES %s RETURNING id",
myTable, cols, values,
)
var id int
if err := db.QueryRow(
query,
thing.val1, thing.val2, thing.val3,
).Scan(&id); err != nil {
panic(err)
}
fmt.Println("ID: ", id)
For multiple inserts:
var cols = "(col1, col2, col3)"
var values = "($1, $2, $3),($4, $5, $6)"
var query = fmt.Sprintf(
"INSERT INTO %s %s VALUES %s RETURNING id",
myTable, cols, values,
)
var ids []int
rows, err := db.Query(
query,
thing.val1, thing.val2, thing.val3,
thing.val4, thing.val5, thing.val6,
)
if err != nil {
panic(err)
}
for rows.Next() {
var id int
if err := rows.Scan(&id); err != nil {
panic(err)
}
ids = append(ids, id)
}
fmt.Println("IDs: ", ids)
res.LastInsertId() is not supported in Postgres Driver. However, It is supported in MySQL Driver.
db.Exec() doesn't return last inserted id but db.QueryRow() does.
For better understanding you can refer this link.
Here, I added one example which might help you.
var id int
err := db.QueryRow("INSERT INTO user (name) VALUES ('John') RETURNING id").Scan(&id)
if err != nil {
...
}
I have a column within my Postgres db for tags which is an array of strings.
I have it defined within a struct in my golang as:
type device struct {
deviceID string
macAddress sql.NullString
name sql.NullString
agentID sql.NullString
groupType sql.NullString
tags []string
normalized bool
normalizedName string
normalizedMacAddress string
}
When I run the scan on the rows as such:
err = rows.Scan(&d.deviceID, &d.name, &d.tags, &d.macAddress, &d.agentID, &d.groupType)
if err != nil {
return nil, err
}
It returns the following error:
"sql: Scan error on column index 2, name "tags": unsupported Scan...+55 more"
So what kinda of wrapper do I need for a string array in order to be an acceptable type?
Use pq.Array when scanning an array:
err = rows.Scan(&d.deviceID, &d.name, pq.Array(&d.tags), &d.macAddress, &d.agentID, &d.groupType)
if err != nil {
return nil, err
}
I have a database calendar with tsrange type from postgres. It allows me to have multiple appointments and time range such as :
["2018-11-08 10:00:00","2018-11-08 10:45:00"]
How do I store this value in a Go variable ?
I tried
var tsrange []string
And when I log tsrange[0] it is empty. What is the proper type for it ?
More code :
rows, err := db.Query("SELECT * FROM appointments")
utils.CheckErr(err)
var id int
var userID int
var tsrange []string
rows.Next()
err = rows.Scan(&id, &userID, &tsrange)
fmt.Println(tsrange[0])
When I replace var tsrange []string with var tsrange string the log is ["2018-11-08 10:00:00","2018-11-08 10:45:00"].
You should be able to retrieve the individual bounds of a range at the sql level.
// replace tsrange_col with the name of your tsrange column
rows, err := db.Query("SELECT id, user_id, lower(tsrange_col), upper(tsrange_col) FROM appointments")
utils.CheckErr(err)
var id int
var userID int
var tsrange [2]time.Time
rows.Next()
err = rows.Scan(&id, &userID, &tsrange[0], &tsrange[1])
fmt.Println(tsrange[0]) // from
fmt.Println(tsrange[1]) // to
I am trying to insert an array into a MongoDB instance using Go. I have the [] string slice in Go and want to convert it into a BSON array to pass it to the DB using the github.com/mongodb/mongo-go-driver driver.
var result bson.Array
for _, data := range myData {
value := bson.VC.String(data)
result.Append(value)
}
This loops over each element of my input data and tries to append it to the BSON array. However the line with the Append() fails with panic: document is nil. How should I do this conversion?
Edit: The code in the question and this answer is no longer relevant because the bson.Array type was deleted from the package. At the time of this edit, the bson.A and basic slice operations should be used to construct arrays.
Use the factory function NewArray to create the array:
result := bson.NewArray()
for _, data := range myData {
value := bson.VC.String(data)
result.Append(value)
}
As mentioned by #Cerise bson.Array has since been deleted. I do this with multiple utility functions as follows:
func BSONStringA(sa []string) (result bson.A) {
result = bson.A{}
for_, e := range sa {
result = append(result, e)
}
return
}
func BSONIntA(ia []string) (result bson.A) {
// ...
}
Converting a slice of string (ids) to BSON array
var objIds bson.A
for _, val := range ids {
objIds = append(objIds, val)
}
log.Println(objIds)
This question already has answers here:
Type converting slices of interfaces
(9 answers)
Closed 3 years ago.
func GetFromDB(tableName string, m *bson.M) interface{} {
var (
__session *mgo.Session = getSession()
)
//if the query arg is nil. give it the null query
if m == nil {
m = &bson.M{}
}
__result := []interface{}{}
__cs_Group := __session.DB(T_dbName).C(tableName)
__cs_Group.Find(m).All(&__result)
return __result
}
call
GetFromDB(T_cs_GroupName, &bson.M{"Name": "Alex"}).([]CS_Group)
runtime will give me panic:
panic: interface conversion: interface is []interface {}, not []mydbs.CS_Group
how convert the return value to my struct?
You can't automatically convert between a slice of two different types – that includes []interface{} to []CS_Group. In every case, you need to convert each element individually:
s := GetFromDB(T_cs_GroupName, &bson.M{"Name": "Alex"}).([]interface{})
g := make([]CS_Group, 0, len(s))
for _, i := range s {
g = append(g, i.(CS_Group))
}
You need to convert the entire hierarchy of objects:
rawResult := GetFromDB(T_cs_GroupName, &bson.M{"Name": "Alex"}).([]interface{})
var result []CS_Group
for _, m := range rawResult {
result = append(result,
CS_Group{
SomeField: m["somefield"].(typeOfSomeField),
AnotherField: m["anotherfield"].(typeOfAnotherField),
})
}
This code is for the simple case where the type returned from mgo matches the type of your struct fields. You may need to sprinkle in some type conversions and type switches on the bson.M value types.
An alternate approach is to take advantage of mgo's decoder by passing the output slice as an argument:
func GetFromDB(tableName string, m *bson.M, result interface{}) error {
var (
__session *mgo.Session = getSession()
)
//if the query arg is nil. give it the null query
if m == nil {
m = &bson.M{}
}
__result := []interface{}{}
__cs_Group := __session.DB(T_dbName).C(tableName)
return __cs_Group.Find(m).All(result)
}
With this change, you can fetch directly to your type:
var result []CS_Group
err := GetFromDB(T_cs_GroupName, bson.M{"Name": "Alex"}, &result)
See also: FAQ: Can I convert a []T to an []interface{}?