Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need help getting past 'reflect: NumField of non-struct type'

I am trying to build a Query struct that will represent data to and from our Cassandra database.

The issue is, I am trying to take a Type as one of my fields in the struct so I can reconstruct it later. I feel like I am really close, but it is giving me some issues. I get a really nasty looking error trying to do this:

2015/11/17 15:42:22 http: panic serving 127.0.0.1:57962: reflect: NumField of non-struct type
goroutine 34 [running]:
net/http.(*conn).serve.func1(0xc820184000, 0x7f36d7459b00, 0xc820180008)
    /usr/lib/go/src/net/http/server.go:1287 +0xb5
reflect.(*rtype).NumField(0x790820, 0xc8200b9a60)
    /usr/lib/go/src/reflect/type.go:660 +0x7b
github.com/relops/cqlr.(*Binding).compile(0xc82004f6f0, 0x77ab60, 0xc8200b9a60, 0x16, 0xc820194140, 0x5, 0x5, 0x0, 0x0)
    /home/jared/dev/go-pp/src/github.com/relops/cqlr/cqlr.go:160 +0xf8
github.com/relops/cqlr.(*Binding).Scan(0xc82004f6f0, 0x77ab60, 0xc8200b9a60, 0x825280)
    /home/jared/dev/go-pp/src/github.com/relops/cqlr/cqlr.go:99 +0x199
main/cassandra/query.Query.RetryingQuery(0x9325e0, 0x19, 0x0, 0x0, 0x0, 0x0, 0x7f36d74580a8, 0x87b120, 0x0, 0x0, ...)
    /home/jared/dev/go-pp/src/main/cassandra/query/query.go:39 +0x39e
main.ViewHosts(0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
    /home/jared/dev/go-pp/src/main/handlers.go:86 +0x1f3
net/http.HandlerFunc.ServeHTTP(0x9a03b0, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
    /usr/lib/go/src/net/http/server.go:1422 +0x3a
main/utils.Logger.func1(0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
    /home/jared/dev/go-pp/src/main/utils/logger.go:32 +0x9c
net/http.HandlerFunc.ServeHTTP(0xc820109200, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
    /usr/lib/go/src/net/http/server.go:1422 +0x3a
github.com/gorilla/mux.(*Router).ServeHTTP(0xc82001aa00, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
    /home/jared/dev/go-pp/src/github.com/gorilla/mux/mux.go:100 +0x29e
net/http.serverHandler.ServeHTTP(0xc82016b1a0, 0x7f36d7459f88, 0xc8200e73f0, 0xc82018e000)
    /usr/lib/go/src/net/http/server.go:1862 +0x19e
net/http.(*conn).serve(0xc820184000)
    /usr/lib/go/src/net/http/server.go:1361 +0xbee
created by net/http.(*Server).Serve
    /usr/lib/go/src/net/http/server.go:1910 +0x3f6

Here is the code

type Query struct {
    query       string
    values      interface{}
    attempts    int
    maxAttempts int
    structType  reflect.Type
}

func NewQuery(query string, t reflect.Type) (q Query) {
    q.query = query
    q.structType = t
    return
}

func (query Query) RetryingQuery() (results []interface{}) {
    var q *gocql.Query
    if query.values != nil {
        q = c.Session.Query(query.query, query.values)
    } else {
        q = c.Session.Query(query.query)
    }

    bindQuery := cqlr.BindQuery(q)

    value := reflect.New(query.structType).Pointer()
    for bindQuery.Scan(&value) {
        results = append(results, value)
    }
    return
}


// setting up and calling the query here (in another file)
var host cmodels.Host
query := query.NewQuery("SELECT * FROM server.host", reflect.TypeOf(host))
queryResults := query.RetryingQuery()

However, with slight modification of the code I get past the error but I am getting a weird result.

func (query Query) RetryingQuery() (results []interface{}) {
    var q *gocql.Query
    if query.values != nil {
        q = c.Session.Query(query.query, query.values)
    } else {
        q = c.Session.Query(query.query)
    }

    bindQuery := cqlr.BindQuery(q)
    value := reflect.New(query.structType)
    for bindQuery.Scan(&value) {
        results = append(results, value)
    }
    return
}

That code above gives me:

[{"flag":22},{"flag":22},{"flag":22},{"flag":22},{"flag":22},...]
like image 962
Jared Mackey Avatar asked Nov 17 '15 22:11

Jared Mackey


1 Answers

Change from:

value := reflect.New(query.structType)
for bindQuery.Scan(&value) {

to:

value := reflect.New(query.structType).Interface()
for bindQuery.Scan(value) {

See here for a full working example (pasted below):

package main

import "reflect"

func Scan(d interface{}) {
    v := reflect.ValueOf(d)
    i := reflect.Indirect(v)
    s := i.Type()
    println(s.NumField()) // will print out 0, if you change Host to have 1 field, it prints out 1
}

func query(t reflect.Type) {
    value := reflect.New(t).Interface()
    Scan(value)
}

type Host struct{}
// type Host struct{int} // comment above line, uncomment this one, and println above will print 1

func main() {
    var h Host
    query(reflect.TypeOf(h))
}

This emulates what your code, plus the clqr library, does (see https://github.com/relops/cqlr/blob/master/cqlr.go#L85-L99 and https://github.com/relops/cqlr/blob/master/cqlr.go#L154-L160). You basically need s := i.Type() to be the TypeOf your Host struct, so if you work backwards from what the clqr code is doing, you can deduce what you need to pass in to the Scan call. And given that the input you have is a reflect.Type, you can deduce how you can get from that Type to the correct kind of object to pass into Scan.

like image 53
Amit Kumar Gupta Avatar answered Nov 04 '22 11:11

Amit Kumar Gupta