Почему gorm db.First() паникует с "неверным адресом памяти или разыменованием нулевого указателя"?

Я не могу понять, сделал ли я что-то глупое или нашел ошибку в gorm. Хотя я очень хорошо понимаю, что означает "недопустимый адрес памяти или разыменование нулевого указателя", я совершенно не понимаю, почему он появляется здесь.

Короче я звоню 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"])

Я рассмотрел отмеченную строку в 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"])

Снова паника, точно в том же месте.

Третья попытка: я просто жестко запрограммировал удостоверение личности для поиска, на случай, если горилла / мукс будет плохо себя вести.

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

Снова паника, точно в том же месте.

Наконец, я протестировал с пустой таблицей базы данных, с заполненной таблицей, запрашивающей идентификатор, который существует, и с заполненной таблицей, запрашивающей идентификатор, который не существует. Во всех трех случаях я получаю одинаковую панику.

Самое интересное в том, что net/http восстанавливает панику, а потом мой notFoundHandler() работает, и я вижу его вывод шаблона в браузере.

В настоящее время я использую драйвер mattn / go-sqlite3.

Моя среда - Fedora 22 x86_64 с cgo 1.4.2, как это предусмотрено в RPM-пакетах Fedora.

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

В чем дело? Откуда эта паника? Как мне это исправить?

2 ответа

Решение

Вы следите за своим глобальным 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")

Иначе, db является nil и приводит к панике.

Это происходит, когда переменная равна нулю.

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

Я подозреваю, что дБ ноль. Я не вижу, где вы это создаете. Вам нужно сделать что-то вроде:

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

и импортируйте "github.com/mattn/go-sqlite3"

В противном случае БД будет нулевым, и возникнет паника.

Другие вопросы по тегам