db.Query with sql join error Scan error on column - sql

error!!!
Scan error on column index 1, name "url": unsupported Scan, storing driver.Value type []uint8 into type *[]handle.Movie
https://gyazo.com/7532a1c3793c892e721054998865609d
https://gyazo.com/278066e6da16f13cd9c56874beb71026
type Movie struct {
ID int
Url string
CategoryID uint
}
type Category struct {
ID int
Name string
Movies []Movie
}
func Connected() []Category {
db := ConnectDB()
defer db.Close()
//sql
query := `SELECT c.name,m.url FROM categories c left join movies m on c.id = m.category_id`
rows, err := db.Query(query)
if err != nil {
log.Fatal(err)
}
var sli []Category
var v1 Category
for rows.Next() {
if err := rows.Scan(&v1.Name, &v1.Movies); err != nil {
log.Fatal(err)
}
sli = append(sli, v1)
}
fmt.Println(sli[0].Movies)
return sli
}
I want to achieve this result!!!
[{1 aaa [https//you...,https//you...],2 bbb [https/you...]}]
I want to get a movie that is linked by category association by slice
-----------------------------PS ---------------------------------
This is what I wanted to do!!!
func Connected() []Category {
db := ConnectDB()
defer db.Close()
//sql
query := `SELECT c.id, c.name, m.id, m.url FROM categories c left join movies m on c.id = m.category_id ORDER BY c.id ASC`
rows, err := db.Query(query)
if err != nil {
log.Fatal(err)
}
var sli []Category
var c Category
var m Movie
for rows.Next() {
if err := rows.Scan(&c.ID, &c.Name, &m.ID, &m.Url); err != nil {
log.Fatal(err)
}
m.CategoryID = c.ID
l := len(sli)
if l > 0 && sli[l-1].ID == c.ID {
sli[l-1].Movies = append(sli[l-1].Movies, m)
} else {
if len(c.Movies) != 0 {
c.Movies = remove(c.Movies, c.Movies[0])
}
c.Movies = append(c.Movies, m)
sli = append(sli, c)
}
}
return sli
}
func remove(ints []Movie, search Movie) []Movie {
result := []Movie{}
for _, v := range ints {
if v != search {
result = append(result, v)
}
}
return result
}
Thanks everyone

well as I see you are trying to store a single URL into an array of Movie struct which is impossible.
your query may return all URLs for each category but each URL is in a single row and you should aggregate them your self.
and as I know you should scan data into Golang default types, not your custom structs. mapping your data into your custom struct is a different thing.
this is a sample code but I don't have access to your database so I can't test it but it should work as you want.
type Movie struct {
ID int
Url string
CategoryID uint
}
type Category struct {
ID int
Name string
Movies []Movie
}
func Connected() []Category {
db := ConnectDB()
defer db.Close()
//sql
query := `SELECT c.id, c.name, m.id, m.url FROM categories c left join movies m on c.id = m.category_id
ORDER BY c.id ASC`
rows, err := db.Query(query)
if err != nil {
log.Fatal(err)
}
var sli []Category
var v1 Category
var m Movie
for rows.Next() {
if err := rows.Scan(&v1.ID, &v1.Name, &m.ID, &m.Url); err != nil {
log.Fatal(err)
}
m.CategoryID = v1.ID
l := len(sli)
if l > 0 && sli[l - 1].ID == v1.ID {
sli[l - 1].Movies = append(sli[l - 1].Movies, m)
} else {
v1.Movies = append(v1.Movies, m)
sli = append(sli, v1)
}
}
fmt.Println(sli[0].Movies)
return sli
}

Related

Not in condition using gorm in golang - dynamic not in condition for select statement

I have two databases Booking and Room. Booking has roomid as one of its field. I wrote a select statement which saves the rows retrieved in result variable as stated below.
var result models.Booking
rows, err := utils.DB.Model(&currRequest).Where("check_in BETWEEN ? AND ? AND check_out BETWEEN ? AND ?", currRequest.CheckIn, currRequest.CheckOut, currRequest.CheckIn, currRequest.CheckOut).Select("room_id").Rows()
for rows.Next() {
utils.DB.ScanRows(rows, &result)
fmt.Println(result.RoomID)
}
Now my result.roomid has values of roomids that satisfy the select statement from the bookings table
My result variable may have multiple room id values. I am able to retrieve the roomid values by looping through the result variable. Now I have to check in my main room database called Room and get those room ids that are not in the result struct. By using the below statement, I am only able to access the first value in result.roomid so the not in condition only considers the first values in result.roomid. How do I do the not in condition for all the values in result.roomid?
rows, err := utils.DB.Model(&models.Room{}).Not(result.RoomID).Select("room_id").Rows()
Full code:
package handlers
import (
"encoding/json"
"fmt"
"net/http"
"server/models"
"server/utils"
"strings"
)
func AvailableRoomsHandler(w http.ResponseWriter, r *http.Request) {
currRequest := &models.Booking{}
err := json.NewDecoder(r.Body).Decode(currRequest)
//check if a valid request has been sent from front end
if err != nil {
//fmt.Println(err)
var resp = map[string]interface{}{"status": false, "message": "Invalid json request"}
json.NewEncoder(w).Encode(resp)
return
}
noOfRoomsOccupied := 0
var notinrooms string
// Use GORM API build SQL
//check if any rooms are available which havent been booked yet in the requested check-in and check-out dates
var result models.Booking
rows, err := utils.DB.Model(&currRequest).Where("check_in BETWEEN ? AND ? AND check_out BETWEEN ? AND ?", currRequest.CheckIn, currRequest.CheckOut, currRequest.CheckIn, currRequest.CheckOut).Select("room_id").Rows()
if err != nil {
json.NewEncoder(w).Encode(err)
fmt.Print("error occured in select statement")
return
} else {
defer rows.Close()
for rows.Next() {
noOfRoomsOccupied = noOfRoomsOccupied + 1
utils.DB.ScanRows(rows, &result)
fmt.Println(result.RoomID)
notinrooms = notinrooms + result.RoomID + ","
}
notinrooms = strings.TrimRight(notinrooms, ",")
fmt.Println(notinrooms)
//calculate the number of rooms in the database
//rows, err := utils.DB.Model(&models.Room{}).Select("room_id").Rows()
res := utils.DB.Find(&models.Room{})
rowcount := res.RowsAffected
fmt.Println(rowcount)
if noOfRoomsOccupied == int(rowcount) {
var resp = map[string]interface{}{"status": false, "message": "no rooms available in the specified time period"}
json.NewEncoder(w).Encode(resp)
return
} else {
noOfRooms := (currRequest.NoOfGuests + currRequest.NoOfChildren) / 2
if (currRequest.NoOfGuests+currRequest.NoOfChildren)%2 == 1 {
noOfRooms = noOfRooms + 1
}
if int(noOfRooms) < int(rowcount)-noOfRoomsOccupied {
fmt.Println("number of rooms to book : ", noOfRooms)
//assign rooms if available
var roomids models.Room
//rows, err := utils.DB.Model(&models.Room{}).Not(result.RoomID).Select("room_id").Rows()
fmt.Println("rooms that can be booked")
rows, err := utils.DB.Model(&models.Room{}).Not(result.RoomID).Select("room_id").Rows()
//rows, err := utils.DB.Model(&models.Room{}).Not([]string{notinrooms}).Select("room_id").Rows()
//map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}
if err != nil {
json.NewEncoder(w).Encode(err)
fmt.Print("error occured in select statement to get room ids to assign")
return
} else {
defer rows.Close()
for rows.Next() {
noOfRoomsOccupied = noOfRoomsOccupied + 1
utils.DB.ScanRows(rows, &roomids)
fmt.Println(roomids.RoomID)
}
}
var success = map[string]interface{}{"message": "Select statement worked well"}
json.NewEncoder(w).Encode(success)
return
}
}
}
}
When I do result.roomid, it only gives the first room id and eliminates only that room id in the above select statement. How do I eliminate all the room ids I found in the booking table in the rooms table data?
I tried splitting the result.roomid values and tried to form a string and gave it in the select statement but that didn't work. I tried looping through every result.roomid and ran the not in a statement but that will not make any sense.
With this code:
var result models.Booking
rows, err := utils.DB.Model(&currRequest).Where("check_in BETWEEN ? AND ? AND check_out BETWEEN ? AND ?", currRequest.CheckIn, currRequest.CheckOut, currRequest.CheckIn, currRequest.CheckOut).Select("room_id").Rows()
if err != nil {
json.NewEncoder(w).Encode(err)
fmt.Print("error occured in select statement")
return
} else {
defer rows.Close()
for rows.Next() {
noOfRoomsOccupied = noOfRoomsOccupied + 1
utils.DB.ScanRows(rows, &result)
//rest of the code
}
}
you only get one row of potentially many rows from the result set. To get all the rows and extract their values, you should use []models.Booking.
result := []models.Booking{}
rows, err := utils.DB.Model(&currRequest).Where("check_in BETWEEN ? AND ? AND check_out BETWEEN ? AND ?", currRequest.CheckIn, currRequest.CheckOut, currRequest.CheckIn, currRequest.CheckOut).Select("room_id").Rows()
if err != nil {
json.NewEncoder(w).Encode(err)
fmt.Print("error occured in select statement")
return
} else {
defer rows.Close()
for rows.Next() {
var b models.Booking
noOfRoomsOccupied = noOfRoomsOccupied + 1
utils.DB.ScanRows(rows, &b)
result = append(result, b)
//rest of the code
}
}
However, since you only need roomid anyway, you could make it easier by using []uint (assuming roomid is of type uint).
result := []uint{}
rows, err := utils.DB.Model(&currRequest).Where("check_in BETWEEN ? AND ? AND check_out BETWEEN ? AND ?", currRequest.CheckIn, currRequest.CheckOut, currRequest.CheckIn, currRequest.CheckOut).Select("room_id").Rows()
if err != nil {
json.NewEncoder(w).Encode(err)
fmt.Print("error occured in select statement")
return
} else {
defer rows.Close()
for rows.Next() {
var rid uint
noOfRoomsOccupied = noOfRoomsOccupied + 1
utils.DB.ScanRows(rows, &rid)
result = append(result, rid)
//rest of the code
}
}
With the result being of type []uint, it would be easier to use it with the Not function (per documentation):
rows, err := utils.DB.Model(&models.Room{}).Not(result).Select("room_id").Rows()

Filtering and sorting an SQL query to recreate a nested struct

I am new to Go and I'm trying to populate a struct called Reliefworker from an SQL query which I can send as a JSON payload.
Essentially I have a Reliefworker who may be assigned to many communities and the community can consist of multiple regions.
I suspect there is a clever way of doing this other than what I intend which is a primitive solution that will add a sort to the SQL (by Community) and create a function that detects if the community being added is different to the previous one, in which case I would create a new community struct type object to be appended.
type Reliefworker struct {
Name string `json:"name"`
Communities []Community `json:"community"`
Deployment_id string `json:"deployment_id"`
}
type Community struct{
Name string `json:"name"`
community_id string `json:"community_id"`
Regions []Region `json:"regions"`
}
type Region struct{
Name string `json:"name"`
Region_id string `json:"region_id"`
Reconstruction_grant string `json:"reconstruction_grant"`
Currency string `json:"currency"`
}
Currently I have created a struct that reflects what I am actually getting from SQL whilst pondering my next move. Perhaps this might be a good stepping stone instead of attempting an on-the-fly transformation ?
type ReliefWorker_community_region struct {
Deployment_id string
Community_title int
Region_name string
Reconstruction_grant int
}
func GetReliefWorkers(deployment_id string) []Reliefworker {
fmt.Printf("Confirm I have a deployment id:%v\n", deployment_id)
rows, err := middleware.Db.Query("select deployment_id, community_title, region_name, reconstruction_grant WHERE Deployment_id=$1", brand_id)
if err != nil {
return
}
for rows.Next() {
reliefworker := Reliefworker{}
err = rows.Scan(&deployment_id, &community_title, &region_name, &reconstruction_grant)
if err != nil {
return
}
}
rows.Close()
return
}
I think a sort makes a lot of sense, primitive solutions can be the most efficient:
func GetReliefWorkers(deployment_id string) []Reliefworker {
// Added sort to query
q := "select worker_name, community_title, region_name, reconstruction_grant WHERE deployment_id=? ORDER BY community_title"
rows, err := middleware.Db.Query(q, deployment_id)
if err != nil {
return
}
defer rows.Close() // So rows get closed even on an error
c := Community{} // To keep track of the current community
cmatrix := [][]string{[]string{}} // Matrix of communities and workers
communities := []Community{} // List of communities
workers := make(map[string]Reliefworker) // Map of workers
var ccount int // Index of community in lists
for rows.Next() {
w := Reliefworker{Deployment_id: deployment_id}
r := Region{}
var ctitle string // For comparison later
err = rows.Scan(&w.Name, &ctitle, &r.Name, &r.Reconstruction_grant)
if err != nil {
return
}
if ctitle != c.Name {
communities = append(communities, c)
c = Community{}
c.Name = ctitle
ccount++
cmatrix = append(cmatrix, []string{})
}
c.Regions = append(c.Regions, r)
cmatrix[ccount] = append(cmatrix[ccount], w.Name)
workers[w.Name] = w
}
for i, c := range communities {
for _, id := range cmatrix[i] {
w := workers[id] // To avoid error
w.Communities = append(w.Communities, c)
workers[id] = w
}
}
out := []Reliefworker{}
for _, w := range workers {
out = append(out, w)
}
return out
}
Though it might make even more sense to create seperate tables for communities, regions, and workers, then query them all with a JOIN: https://www.w3schools.com/sql/sql_join_inner.asp
UPDATE: Since you only want to retrieve one Reliefworker, would something like this work?
type ReliefWorker struct {
Name string `json:"name"`
Communities []Community `json:"community"`
}
type Community struct {
Name string `json:"name"`
Regions []Region `json:"regions"`
}
type Region struct {
Name string `json:"name"`
Region_id string `json:"region_id"`
Reconstruction_grant int `json:"reconstruction_grant"`
Currency string `json:"currency"`
}
func GetReliefWorkers(deployment_id string) Reliefworker {
reliefworker := Reliefworker{}
communities := make(map[string]Community)
rows, err := middleware.Db.Query("select name, community_title, region_name, region_id, reconstruction_grant WHERE Deployment_id=$1", deployment_id)
if err != nil {
if err == sql.ErrNoRows {
fmt.Printf("No records for ReliefWorker:%v\n", deployment_id)
}
panic(err)
}
defer rows.Close()
for rows.Next() {
c := Community{}
r := Region{}
err = rows.Scan(&reliefworker.Name, &c.Name, &r.Name, &r.Region_id, &r.Reconstruction_grant)
if err != nil {
panic(err)
}
if _, ok := communities[c.Name]; ok {
c = communities[c.Name]
}
c.Regions = append(c.Regions, r)
communities[c.Name] = c
}
for _, c := range commmunities {
reliefworker.Communities = append(reliefworker.Communities, c)
}
return reliefworker
}
Ok, my crude solution doesn't contain a shred of the intelligence #Absentbird demonstrates but I'm here to learn.
#Absentbird I love your use of maps and multidimensional arrays to hold a matrix of communities and workers. I will focus on making this part of my arsenal over the weekend.
I can accept and adapt #Absentbird's solution once I have a solution to why it gives the error "cannot assign to struct field workers[id].Communities in mapcompilerUnaddressableFieldAssign" for the line workers[id].Communities = append(workers[id].Communities, c)
Firstly apologies as I had to correct two things. Firstly I only needed to return ReliefWorkers (not an array of ReliefWorkers). Secondly ReliefWorker struct did not need to contain the Deployment_id since I already knew it.
I am new to Go so I'd really appreciate feedback on what I can do to better leverage the language and write more concise code.
My structs and solution is currently as follows:
type ReliefWorker struct {
Name string `json:"name"`
Communities []Community `json:"community"`
}
type Community struct {
Name string `json:"name"`
Regions []Region `json:"regions"`
}
type Region struct {
Name string `json:"name"`
Region_id string `json:"region_id"`
Reconstruction_grant int `json:"reconstruction_grant"`
Currency string `json:"currency"`
}
type ReliefWorker_community_region struct {
Name string
Community_title string
Region_name string
Reconstruction_grant int
}
func GetReliefWorkers(deployment_id string) Reliefworker {
var reliefworker Reliefworker
var communitiesOnly []string
var name string
var allReliefWorkerData []ReliefWorker_community_region
rows, err := middleware.Db.Query("select name, community_title, region_name, reconstruction_grant WHERE Deployment_id=$1", deployment_id)
for rows.Next() {
reliefWorker_community_region := ReliefWorker_community_region{}
err = rows.Scan(&reliefWorker_community_region.Name, &reliefWorker_community_region.Community_title, &reliefWorker_community_region.Region_name, &reliefWorker_community_region.Reconstruction_grant)
if err != nil {
panic(err)
}
name = reliefWorker_community_region.Name
allReliefWorkerData = append(allReliefWorkerData, reliefWorker_community_region)
communitiesOnly = append(communitiesOnly, reliefWorker_community_region.Community_title) //All communities go in here, even duplicates, will will create a unique set later
}
rows.Close()
if err != nil {
if err == sql.ErrNoRows {
fmt.Printf("No records for ReliefWorker:%v\n", deployment_id)
}
panic(err)
}
var unique []string //Use this to create a unique index of communities
for _, v := range communitiesOnly {
skip := false
for _, u := range unique {
if v == u {
skip = true
break
}
}
if !skip {
unique = append(unique, v)
}
}
fmt.Println(unique)
reliefworker.Name = name
var community Community
var communities []Community
for _, v := range unique {
community.Name = v
communities = append(communities, community)
}
// Go through each record from the database held within allReliefWorkerData and grab every region belonging to a Community, when done append it to Communities and finally append that to ReliefWorker
for j, u := range communities {
var regions []Region
for i, v := range allReliefWorkerData {
if v.Community_title == u.Name {
var region Region
region.Name = v.Region_name
region.Reconstruction_grant = v.Reconstruction_grant
regions = append(regions, region)
}
}
communities[j].Regions = regions
}
reliefworker.Communities = communities
return reliefworker
}

How to handle nullable Postgres JSONB data and parse it as JSON

DB Records
---------------------------------------------------------
| id | test_json |
---------------------------------------------------------
| 1 | NULL |
---------------------------------------------------------
| 2 | { "firstName": "Hello", "lastName": "World" } |
---------------------------------------------------------
I have JSONB column in postgres which can be NULL. I want to read this records DB in golang and send it to the client.
I'm getting below error on SQL scan:
sql: Scan error on column index 2, name "test_json": unsupported Scan, storing driver.Value type []uint8 into type *models.TestJSONNullable
exit status 1
I'm using echo web server.
package models
import (
"fmt"
"github.com/lib/pq"
"encoding/json"
)
type TestJson struct {
First_name *string `json:"firstName"`
Last_name *string `json:"lastName"`
}
type TestJSONNullable struct {
Valid bool
}
func (i *TestJSONNullable) UnmarshalJSON(data []byte) error {
if string(data) == "null" {
i.Valid = false
return nil
}
// The key isn't set to null
var temp *TestJson
if err := json.Unmarshal(data, &temp); err != nil {
return err
}
i.Valid = true
return nil
}
type Test01 struct {
Id string `json:"id"`
Test_json *TestJSONNullable `json:"testJson"`
}
func (db *DB) TestRecords () ([]*Test01, error) {
rows, err := db.Query("SELECT id, test_json FROM table_1 where success = true")
if err != nil {
log.Fatal(err)
return nil, err
}
defer rows.Close()
recs := []*Test01{}
for rows.Next() {
r := new(Test01)
err := rows.Scan(&r.Id, &r.Test_json)
if err != nil {
log.Fatal(err)
return nil, err
}
recs = append(recs, r)
}
if err = rows.Err(); err != nil {
log.Fatal(err)
return nil, err
}
return recs, nil
}
Here's another solution: You can implement a nullable type for raw JSON data, similar to sql.NullString, and use it as a scan destination. In this scenario, you will first check whether the value is null, and then unmarshal it only if it's not null. For instance, the NullRawMessage type from github.com/soroushj/sqlt is a nullable json.RawMessage which can be used for this purpose.
Here's an example:
package main
import (
"database/sql"
"log"
_ "github.com/lib/pq"
"github.com/soroushj/sqlt"
)
func main() {
db, err := sql.Open("postgres", "dbname=dbname user=user password=password sslmode=disable")
if err != nil {
log.Fatal(err)
}
row := db.QueryRow(`SELECT test_json FROM my_table WHERE id = $1`, 1)
testJSON := sqlt.NullRawMessage{}
err = row.Scan(&testJSON)
if err != nil {
log.Fatal(err)
}
if testJSON.Valid {
// test_json is not null
// Unmarshal testJSON.RawMessage
} else {
// test_json is null
}
}
After doing some research i found the solution.
type TestJSONMap map[string]interface{}
func (t TestJSONMap) Value() (driver.Value, error) {
j, err := json.Marshal(t)
return j, err
}
func (p *TestJSONMap) Scan(val interface{}) error {
value, ok := val.([]byte)
if !ok {
return errors.New("Type assertion .([]byte) failed.")
}
var i interface{}
err := json.Unmarshal(value, &i)
if err != nil {
return err
}
*p, ok = i.(map[string]interface{})
if !ok {
return errors.New("Type assertion .(map[string]interface{}) failed.")
}
return nil
}
type Test01 struct {
Id string `json:"id"`
Test_json *TestJSONMap `json:"testJson"`
}
Got help from https://coussej.github.io/2016/02/16/Handling-JSONB-in-Go-Structs/
Instead of using struct to store values, You can use map.
type TestJson struct {
First_name *string `json:"firstName"`
Last_name *string `json:"lastName"`
}
You can use interface as
var TestJson interface{}
err := json.Unmarshal(b, &TestJson)
On the other side you can also use Dynamic creation of structs as well.
Something like
m := map[string]interface{}{
"key": "value",
}
And rather than having TestJSONNullable it would be better to have Switch case while saving data.
switch v := TestJson.(type) {
case int:
case float64:
case string:
default:
// i isn't one of the types above
}
Look into this for more details--> https://godoc.org/encoding/json#Unmarshal

golang missing len argument

How to make a row as map[string]map[string]interface{}
cannot use s.ID (type string) as type map[string]interface {} in assignment
var row = make(map[string]map[string]interface{})
Listservers
func ListServers() (map[string]map[string]interface{}, error) {
listOptions := servers.ListOpts{}
pager := servers.List(GetClientCompute(), listOptions)
err := pager.EachPage(func(page pagination.Page) (bool, error) {
serverList, err := servers.ExtractServers(page)
if err != nil {
fmt.Println(err)
}
for _, s := range serverList {
row["ID"] = s.ID <---- error is here
row["Name"] = s.Name <---- error is here
if s.Addresses["public"] != nil {
for _, i := range s.Addresses["public"].([]interface{}) {
temp := i.(map[string]interface{})
if temp["version"].(float64) == 4 {
row["IP"] = temp["addr"]
}
}
}
t, _ := time.Parse(time.RFC3339, s.Created)
row["Flavor"] = s.Flavor
row["Created"] = time.Now().Sub(t) <---- error is here
row["Status"] = s.Status <---- error is here
}
return false, nil
})
// fmt.Println(lists)
return row, err
}
The row is a SLICE of map[string]interface{}. You need to provide the length when you initialize the slice like this:
row := make([]map[string]interface{}, 0)
The index of a slice MUST be an integer, That's why you encounter the second problem mentioned in your comment.
Let's suppose serverList is a slice. You code may be modified as:
rows := make([]map[string]interface{}, 0) // create a slice
// ... codes omitted
for _, s := range serverList {
row := make(map[string]interface{}) // create an item
row["ID"] = s.ID
row["Name"] = s.Name
// ... codes omitted
row["Flavor"] = s.Flavor
row["Created"] = time.Now().Sub(t)
row["Status"] = s.Status
rows = append(rows, row) // append the item to the slice
}
return rows, err

sql: scan row(s) with unknown number of columns (select * from ...)

I have a table t containing a lot of columns, and my sql is like this: select * from t. Now I only want to scan one column or two from the wide returned row set. However, the sql.Scan accepts dest ...interface{} as arguments. Does it mean I have to scan everything and use only the column I needed?
I know I could change the sql from select * to select my_favorite_rows, however, in this case, I have no way to change the sql.
You can make use of Rows.Columns, e.g.
package main
import (
"database/sql"
"fmt"
"github.com/lib/pq"
)
type Vehicle struct {
Id int
Name string
Wheels int
}
// VehicleCol returns a reference for a column of a Vehicle
func VehicleCol(colname string, vh *Vehicle) interface{} {
switch colname {
case "id":
return &vh.Id
case "name":
return &vh.Name
case "wheels":
return &vh.Wheels
default:
panic("unknown column " + colname)
}
}
func panicOnErr(err error) {
if err != nil {
panic(err.Error())
}
}
func main() {
conn, err := pq.ParseURL(`postgres://docker:docker#172.17.0.2:5432/pgsqltest?schema=public`)
panicOnErr(err)
var db *sql.DB
db, err = sql.Open("postgres", conn)
panicOnErr(err)
var rows *sql.Rows
rows, err = db.Query("select * from vehicle")
panicOnErr(err)
// get the column names from the query
var columns []string
columns, err = rows.Columns()
panicOnErr(err)
colNum := len(columns)
all := []Vehicle{}
for rows.Next() {
vh := Vehicle{}
// make references for the cols with the aid of VehicleCol
cols := make([]interface{}, colNum)
for i := 0; i < colNum; i++ {
cols[i] = VehicleCol(columns[i], &vh)
}
err = rows.Scan(cols...)
panicOnErr(err)
all = append(all, vh)
}
fmt.Printf("%#v\n", all)
}
For unknown length of columns but if you're sure about their type,
cols, err := rows.Columns()
if err != nil {
log.Fatal(err.Error())
}
colLen := len(cols)
vals := make([]interface{}, colLen)
for rows.Next() {
for i := 0; i < len(colLen); i++ {
vals[i] = new(string)
}
err := rows.Scan(vals...)
if err != nil {
log.Fatal(err.Error()) // if wrong type
}
fmt.Printf("Column 1: %s\n", *(vals[0].(*string))) // will panic if wrong type
}
PS: Not recommended for prod