Jagrati
Jagrati

Reputation: 12222

channel of type interface not receiving value in golang with MySql

I am new to golang. I am trying to do concurrent queries to mysql db with golang. I know channels can be of the type interface. When I print tableData (type map) in RunQuery function I am getting the result. I am sending tableData to ch i.e. channel of type interface. In function getdataList I am not getting any value in ch. I don't understand what I am doing wrong.

Following is my code:

package main

import (
    "database/sql"
    "fmt"
    "net/http"
    _ "github.com/go-sql-driver/mysql"
    "log"
)


var db *sql.DB

func getdataList(id int) {
        ch := make(chan interface{})
        done := make (chan bool)
        RunQuery(ch,"select id,name, last_name,first_name from persons where id= ?", id)
        go func() {
            for {
            x, ok := <-ch //I am not getting any data in channel here
            if ok {
                fmt.Println(x)
            }else {
                fmt.Println("done")
                done <- true
                return
            }

        }
        }()
    }

func RunQuery (ch chan interface{}, query string, param interface{}) {

    stmt, err := db.Prepare(query)
    if err != nil {
                panic(err.Error())
            }
    defer stmt.Close()
    rows, err := stmt.Query(param)
    columns, err := rows.Columns()
    if err != nil {
        fmt.Println("Failed to get columns", err)
        return
    }
    count := len(columns)
    tableData := make([]map[string]interface{}, 0)
    values := make([]interface{}, count)
    valuePtrs := make([]interface{}, count)
    for rows.Next() {
      for i := 0; i < count; i++ {
          valuePtrs[i] = &values[i]
      }
      rows.Scan(valuePtrs...)
      entry := make(map[string]interface{})
      for i, col := range columns {
          var v interface{}
          val := values[i]
          b, ok := val.([]byte)
          if ok {
              v = string(b)
          } else {
              v = val
          }
          entry[col] = v
      }
      tableData = append(tableData, entry)
  }
    fmt.Pritln(tableData) //here I am getting data in map
    ch <- tableData
}


func dbtest(w http.ResponseWriter, req *http.Request) {

    go getdataList(2)
    go getdataList(3)
}

func main() {
    var err error
    db, err = sql.Open("mysql", "root:@/dbName")
    if err != nil {
        panic(err.Error())  
    }
    defer db.Close()

    http.HandleFunc("/dbTest", dbtest)

    log.Fatal(http.ListenAndServe(":8080", nil))

}

Upvotes: 0

Views: 1642

Answers (1)

abhink
abhink

Reputation: 9136

The problem with your code is that it is blocking the execution flow before data can be read from the channel. When you call RunQuery from getdataList, RunQuery tries to send data over channel ch. However, nothing is reading from ch because the code to read from it is in getdataList and it's below the call to RunQuery.

Therefore, RunQuery never returns and the goroutine to read from ch never fires. To fix, You can try running RunQuery as a goroutine as well:

func getdataList(id int) {
        ch := make(chan interface{})
        done := make (chan bool)
        // run in a goroutine
        go RunQuery(ch,"select id,name, last_name,first_name from persons where id= ?", id)
        go func() {
            for {
            x, ok := <-ch //I am not getting any data in channel here
            if ok {
                fmt.Println(x)
            }else {
                fmt.Println("done")
                done <- true
                return
            }

        }
    }()
}

There is another issue in your code. You are never closing ch. This may result in deadlock. The most ideal place to do this appears to be in RunQuery:

func RunQuery (ch chan interface{}, query string, param interface{}) {
    // ...
    ch <- tableData
    close(ch)
}

Upvotes: 1

Related Questions