在GO中重用数据库连接和“无效的内存地址”错误

时间:2015-03-10 13:30:44

标签: go

我是GO的新手,并且似乎无法在全球范围内重复使用数据库连接来查找有关错误的任何信息。这是我的代码的简化版本,用于警告错误:

package main

import (
    _ "github.com/denisenkom/go-mssqldb"
    "database/sql"
)

var debug = flag.Bool("debug", false, "enable debugging")
var password = flag.String("password", "*****", "the database password")
var port *int = flag.Int("port", 1433, "the database port")
var server = flag.String("server", "localhost", "the database server")
var user = flag.String("user", "*****", "the database user")
var db *sql.DB

func main() {
    // Parse the incoming flags to set variables.
    flag.Parse()

    // Output the database variables if the debug flag is set.
    if *debug {
        fmt.Printf(" password:%s\n", *password)
        fmt.Printf(" port:%d\n", *port)
        fmt.Printf(" server:%s\n", *server)
        fmt.Printf(" user:%s\n", *user)
    }

    // Build out the connection string to the database, and then open the connection to the database.
    connString := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d", *server, *user, *password, *port)
    if *debug { fmt.Printf(" connString:%s\n", connString) }
    db, err := sql.Open("mssql", connString)
    if err != nil { log.Fatal("Open connection failed:", err.Error()) }
    err = db.Ping()
    if err != nil {
        fmt.Println("Cannot connect: ", err.Error())
        return
    }

    // Close the connection when we're all done.
    defer db.Close()

    // Truncate the existing records in the ListHubFeed
    res, err := db.Exec( `
        TRUNCATE TABLE foo
    ` )
    _ = res

    if err != nil {
        fmt.Println( "TRUNCATE TABLE failed", err )
    }

    eligible := verifyEligibility( "foo" )

}

func verifyEligibility( email string ) ( success bool ) {

    //Run each check individually, and if any one fails, return false and stop processing additional checks

    // if the routing email is bad, then we can't create an account, so kick it out.
    if( !govalidator.IsEmail( email ) ){
        writeToLog( email )
        return false
    }

    return true;

}

func writeToLog( tolog string ) {
    res, err := db.Exec(`
        INSERT INTO Log ( 
            Email
        ) VALUES (
            ?,
            ?,
            ?,
            ?
        )` , tolog )
    if err != nil {
        fmt.Println( "insert failed", err )
    }
    _ = res
}

那真是丑陋的伪代码,但问题是TRUNCATE中的main语句运行得很好,但是在调用verifyEligibility()然后尝试记录条目之后writeToLog()函数,我收到以下错误:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x0 pc=0x46d485]

goroutine 1 [running]:
database/sql.(*DB).conn(0x0, 0xc082044088, 0x0, 0x0)
        c:/go/src/database/sql/sql.go:634 +0x7b5
database/sql.(*DB).exec(0x0, 0x742730, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        c:/go/src/database/sql/sql.go:884 +0x9d
database/sql.(*DB).Exec(0x0, 0x742730, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        c:/go/src/database/sql/sql.go:875 +0xd4
main.writeToLog(0xc082004a20, 0x11, 0x70f370, 0x5, 0x0, 0x0, 0x777ef0, 0x24, 0x71ba70, 0x3)

我无法找到有关此错误的任何信息,因为它与go-mssqldb有关。 db被声明为全局,并在main中打开,因此我无法在writeToLog()中找出我无法使用的原因。

1 个答案:

答案 0 :(得分:8)

您的问题是db指针在nil函数的上下文中为writeToLog()。罪魁祸首是这一行:

db, err := sql.Open("mssql", connString)

在这里,您将短变量声明运算符(:=)与赋值运算符(=)混淆。

此指令使用db局部变量覆盖db全局变量,只能在main函数中访问(这称为 shadowing ,并且还可以工作包名称等)。因此,db中使用的writeToLog变量永远不会被初始化,因此nil pointer dereference错误。

要解决这个问题,你只需要修改你的声明,使其成为一种矫揉造作:

var err error
db, err = sql.Open("mssql", connString)