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

huangapple go评论87阅读模式
英文:

Reusing Database Connection in GO and 'invalid memory address' errors

问题

我是你的中文翻译助手,以下是你提供的代码的翻译:

package main

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

var debug = flag.Bool("debug", false, "启用调试")
var password = flag.String("password", "*****", "数据库密码")
var port *int = flag.Int("port", 1433, "数据库端口")
var server = flag.String("server", "localhost", "数据库服务器")
var user = flag.String("user", "*****", "数据库用户")
var db *sql.DB

func main() {
	// 解析传入的标志以设置变量。
	flag.Parse()

	// 如果设置了调试标志,则输出数据库变量。
	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)
	}

	// 构建数据库连接字符串,然后打开数据库连接。
	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("打开数据库连接失败:", err.Error()) }
	err = db.Ping()
	if err != nil {
		fmt.Println("无法连接数据库:", err.Error())
		return
	}

	// 完成后关闭数据库连接。
	defer db.Close()

	// 清空 ListHubFeed 中的现有记录
	res, err := db.Exec(`
		TRUNCATE TABLE foo
	`)
	_ = res

	if err != nil {
		fmt.Println("清空表格失败", err)
	}

	eligible := verifyEligibility("foo")

}

func verifyEligibility(email string) (success bool) {

	// 逐个检查每个条件,如果有任何一个失败,则返回 false 并停止处理其他检查

	// 如果路由电子邮件无效,则无法创建帐户,因此将其排除。
	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("插入失败", err)
	}
	_ = res
}

这段代码中的问题是,在 writeToLog() 函数中无法访问到全局变量 db。这是因为在 writeToLog() 函数中重新声明了一个名为 db 的局部变量,它会遮蔽全局变量 db。为了解决这个问题,你可以将 db 作为参数传递给 writeToLog() 函数,而不是在函数内部重新声明一个新的变量。修改后的代码如下:

func writeToLog(db *sql.DB, tolog string) {
	res, err := db.Exec(`
		INSERT INTO Log ( 
			Email
		) VALUES (
			?,
			?,
			?,
			?
		)`, tolog)
	if err != nil {
		fmt.Println("插入失败", err)
	}
	_ = res
}

然后,在调用 writeToLog() 函数时,将 db 作为参数传递进去:

writeToLog(db, email)

这样就可以在 writeToLog() 函数中访问到全局变量 db 了。希望这可以解决你的问题!

英文:

I'm new to GO, and can't seem to find any information on an error with reusing a database connection in a global. Here's a simplified version of my code that caues the error:

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
}

That's really ugly psuedocode, but the point is that the TRUNCATE statements in main run just fine, but after calling verifyEligibility() and then attempting to log the entry in the writeToLog() function, I get the following error:

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)

I can't find any information on this error as it relates to go-mssqldb. db is declared as a global, and is opened in main, so I can't figure out why it's not available to me in writeToLog().

答案1

得分: 10

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

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

在这里,你将短变量声明操作符(:=)与赋值操作符(=)混淆了。

这个语句用一个db局部变量覆盖了全局变量db,只能在main函数中访问(这被称为shadowing,也适用于包名等)。因此,在writeToLog中使用的db变量从未初始化,导致了nil指针解引用错误。

要修复它,你只需要修复声明部分,使其成为一个赋值操作:

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

Your problem is that the db pointer is nil in the context of your writeToLog() function. The culprit is this line:

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

Here, you are confusing the short variable declaration operator (:=) with the assignment operator (=).

This instruction override the db global variable with a db local variable, only accessible in your main function (this is called shadowing, and also work with package names, etc). Consequently, the db variable used in writeToLog is never initialized, hence the nil pointer dereference error.

To fix it, you just have to fix your declaration for it to be an affectation:

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

huangapple
  • 本文由 发表于 2015年3月10日 21:30:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/28965092.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定