为什么 gorm db.First() 对 "invalid memory address or nil pointer dereference" 感到恐慌?

Why does gorm db.First() panic with "invalid memory address or nil pointer dereference"?

我不知道我是不是做了什么蠢事,还是发现了 gorm 中的错误。虽然我非常清楚 "invalid memory address or nil pointer dereference" 的含义,但我完全不明白它为什么会出现在这里。

简而言之,我打电话给 db.First(),但没有明显原因收到恐慌。

我的代码的相关部分:

package main

import (
    "fmt"
    "github.com/gorilla/mux"
    "github.com/jinzhu/gorm"
    "net/http"
    "os"
)

type message struct {
    gorm.Model
    Title string
    Body  string `sql:"size:0"` // blob
}

var db = gorm.DB{} // garbage

func messageHandler(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    m := message{}
    query := db.First(&m, vars["id"])
    if query.Error != nil {
        if query.Error == gorm.RecordNotFound {
            notFoundHandler(w, r)
            return
        } else {
            fmt.Fprintf(os.Stderr, "database query failed: %v", query.Error)
            internalServerErrorHandler(w, r)
            return
        }
    }

    // actually do something useful
}

func main() {
    db, err := gorm.Open("sqlite3", "/tmp/gorm.db")
    // ...
}

db在包中main()打开,作为包变量存储。这看起来不是很干净,但它似乎工作...

恐慌:

2015/07/16 20:56:12 http: panic serving [::1]:37326: runtime error: invalid memory address or nil pointer dereference
goroutine 26 [running]:
net/http.func·011()
        /usr/lib/golang/src/net/http/server.go:1130 +0xbb
github.com/jinzhu/gorm.(*DB).First(0xd28720, 0x79f220, 0xc2080b2600, 0xc2080ef220, 0x1, 0x1, 0xd)
        /home/error/go/src/github.com/jinzhu/gorm/main.go:200 +0x154
main.messageHandler(0x7f4f2e785bd8, 0xc208051c20, 0xc208035790)
        /home/error/go/src/myproject/messages.go:28 +0x2c1
net/http.HandlerFunc.ServeHTTP(0x9c3948, 0x7f4f2e785bd8, 0xc208051c20, 0xc208035790)
        /usr/lib/golang/src/net/http/server.go:1265 +0x41
github.com/gorilla/mux.(*Router).ServeHTTP(0xc2080d9630, 0x7f4f2e785bd8, 0xc208051c20, 0xc208035790)
        /home/error/go/src/github.com/gorilla/mux/mux.go:98 +0x297
net/http.serverHandler.ServeHTTP(0xc2080890e0, 0x7f4f2e785bd8, 0xc208051c20, 0xc208035790)
        /usr/lib/golang/src/net/http/server.go:1703 +0x19a
net/http.(*conn).serve(0xc208051b80)
        /usr/lib/golang/src/net/http/server.go:1204 +0xb57
created by net/http.(*Server).Serve
        /usr/lib/golang/src/net/http/server.go:1751 +0x35e

...我代码的第 28 行是 query := db.First(&m, vars["id"])

我查看了 the noted line in gormFirst() 函数,但这也不是很明显。

    return newScope.Set("gorm:order_by_primary_key", "ASC").
        inlineCondition(where...).callCallbacks(s.parent.callback.queries).db

为了弄清楚可能发生了什么,我对代码进行了以下更改:

第一次尝试:它是否抱怨被传递了一个字符串?让我们给它一个整数。毕竟例子用的是整数

    id, _ := strconv.Atoi(vars["id"])
    query := db.First(&m, id)

再次恐慌,在完全相同的地方。

第二次尝试:我是否以错误的方式创建变量 m?也许真的需要先分配new

    m := new(message)
    query := db.First(m, vars["id"])

再次恐慌,在完全相同的地方。

第三次尝试:我只是硬编码要查找的 ID,以防万一 gorilla/mux 行为不当。

    m := message{}
    query := db.First(&m, 3)

再次恐慌,在完全相同的地方。

最后,我使用空数据库 table 进行了测试,填充的 table 请求存在的 ID,填充的 table 请求不存在的 ID。在这三种情况下,我都收到了同样的恐慌。

最有趣的部分是显然 net/http 正在恢复恐慌,然后我的 notFoundHandler() 运行并且我在浏览器中看到它的模板输出。

我目前正在使用 mattn/go-sqlite3 驱动程序。

我的环境是 Fedora 22 x86_64,Fedora RPM 包中提供了 cgo 1.4.2。

$ go version
go version go1.4.2 linux/amd64

$ go env
GOARCH="amd64"
GOBIN=""
GOCHAR="6"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/error/go"
GORACE=""
GOROOT="/usr/lib/golang"
GOTOOLDIR="/usr/lib/golang/pkg/tool/linux_amd64"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"

这是怎么回事?这种恐慌从何而来?我该如何解决?

当变量为 nil 时会发生这种情况。

query := db.First(&m, vars["id"])

我怀疑db为零。我看不到你在哪里创建它。您需要执行以下操作:

db, err := gorm.Open("sqlite3", "/tmp/gorm.db")

并导入 "github.com/mattn/go-sqlite3"

否则db将为nil,会出现panic。

您正在跟踪全局 db 变量:

var db = gorm.DB{} // garbage

您在 main() 中的初始化应更改为:

var err error
// Note the assignment and not initialise + assign operator
db, err = gorm.Open("sqlite3", "/tmp/gorm.db")

否则,dbnil 并导致恐慌。