为啥 gorm db.First() 会因“无效的内存地址或 nil 指针取消引用”而恐慌? [复制]

Posted

技术标签:

【中文标题】为啥 gorm db.First() 会因“无效的内存地址或 nil 指针取消引用”而恐慌? [复制]【英文标题】:Why does gorm db.First() panic with "invalid memory address or nil pointer dereference"? [duplicate]为什么 gorm db.First() 会因“无效的内存地址或 nil 指针取消引用”而恐慌? [复制] 【发布时间】:2015-10-06 16:24:08 【问题描述】:

我不知道我是否做了一些愚蠢的事情,或者我是否在 gorm 中发现了一个错误。虽然我非常清楚“无效的内存地址或 nil 指针取消引用”是什么意思,但我完全不明白为什么它会出现在这里。

简而言之,我拨打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 gorm 和First() 函数,但这也不是很明显。

    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)

再次恐慌,在同一个地方。

最后,我测试了一个空的数据库表,一个填充的表请求一个存在的 ID,一个填充的表请求一个不存在的 ID。在所有三种情况下,我都会收到同样的恐慌。

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

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

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

$ 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"

发生了什么事?这种恐慌从何而来?我该如何解决?

【问题讨论】:

我的建议是将newScope.Set("gorm:order_by_primary_key", "ASC").inlineCondition(where...).callCallbacks(s.parent.callback.queries).db 的每个部分分配给变量,然后打印出它们的值以找到nil。这意味着要低头弄脏并修改 gorm 的代码以进行调试……不幸的是,这有时需要发生。 ????详细说明。 【参考方案1】:

您正在隐藏您的全局 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 并导致恐慌。

【讨论】:

太棒了! (你,不是我......)顺便说一句,谢谢你的写作。我发现Approximating html/template inheritance 特别有帮助。【参考方案2】:

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

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

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

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

并导入“github.com/mattn/go-sqlite3”

否则 db 将为 nil 并且会发生恐慌。

【讨论】:

以上是关于为啥 gorm db.First() 会因“无效的内存地址或 nil 指针取消引用”而恐慌? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥 GORM 不保存我的对象?

当使用带有 AVX-512 加载和存储的屏蔽寄存器时,是不是会因对屏蔽元素的无效访问而引发错误?

为啥 sed 会因国际字符而失败以及如何解决?

为啥标量值函数会因空参数而失败?

套接字:为啥阻塞 read() 会因 ENOTCONN 而失败?

为啥这个应用程序会因运行时错误而崩溃?