carusot42
carusot42

Reputation: 1211

Package-wide variable assignment in Golang's init() function

I want to initialize a package-wide variable in some Go code to connect to a database, but I keep getting a nil pointer exception, so clearly the assignment isn't occurring properly. This throws an error:

package main

import (
"fmt"
"database/sql"

_ "github.com/lib/pq"
)

var connection *sql.DB

func init() {
    connectionString := "host=172.17.0.3 dbname=postgres user=postgres password=postgres port=5432 sslmode=disable"

    connection, err := sql.Open(
        "postgres",
        connectionString,
    )
    check(err)

    err = connection.Ping()
    check(err)
}

func main() {
    TestConnect()
}

func TestConnect() {
    var pid int

    err := connection.QueryRow("SELECT pg_backend_pid()").Scan(&pid)
    check(err)
    fmt.Printf("pid: %v", pid)
}

func check(err error) {
    if err != nil {
        panic(err)
    }
}

Here's the error:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4c1a1a]

goroutine 1 [running]:
database/sql.(*DB).conn(0x0, 0x70b7c0, 0xc4200102b8, 0x1, 0xc420055e08, 0x4c28df, 0xc4200b0000)
    /usr/local/go/src/database/sql/sql.go:896 +0x3a
database/sql.(*DB).query(0x0, 0x70b7c0, 0xc4200102b8, 0x630335, 0x17, 0x0, 0x0, 0x0, 0x101, 0x741c80, ...)
    /usr/local/go/src/database/sql/sql.go:1245 +0x5b
database/sql.(*DB).QueryContext(0x0, 0x70b7c0, 0xc4200102b8, 0x630335, 0x17, 0x0, 0x0, 0x0, 0x0, 0x8, ...)
    /usr/local/go/src/database/sql/sql.go:1227 +0xb8
database/sql.(*DB).QueryRowContext(0x0, 0x70b7c0, 0xc4200102b8, 0x630335, 0x17, 0x0, 0x0, 0x0, 0xc420010cb0)
    /usr/local/go/src/database/sql/sql.go:1317 +0x90
database/sql.(*DB).QueryRow(0x0, 0x630335, 0x17, 0x0, 0x0, 0x0, 0x0)
    /usr/local/go/src/database/sql/sql.go:1325 +0x7c
main.TestConnect()
    /home/tom/go/src/go-rest/ignore/connect.go:32 +0x82
main.main()
    /home/tom/go/src/go-rest/ignore/connect.go:26 +0x20
exit status 2

However, if I switch it up so I can use the = operator on connection instead of :=...

package main

import (
    "fmt"
    "database/sql"

    _ "github.com/lib/pq"
)

var connection *sql.DB

func init() {
    connectionString, err := GetConnectionString()

    connection, err = sql.Open(
        "postgres",
        connectionString,
    )
    check(err)

    err = connection.Ping()
    check(err)
}

func main() {
    TestConnect()
}

func TestConnect() {
    var pid int

    err := connection.QueryRow("SELECT pg_backend_pid()").Scan(&pid)
    check(err)
    fmt.Printf("pid: %v", pid)
}

func GetConnectionString() (string, error) {
    return "host=172.17.0.3 dbname=postgres user=postgres password=postgres port=5432 sslmode=disable", nil
}

func check(err error) {
    if err != nil {
        panic(err)
    }
}

Then everything works as expected, and the variable is properly assigned. Because the err variable is previously undefined, I have to use := for assignment, but this seems to assume that I'm initializing a function-scoped connection variable, and not trying to assign to the package-scoped connection variable. Is there a way to force this assignment in Go? As it stands, I need to write useless, boilerplate code to get this to work the right way, and it seems like there should be a better way.

Of course, the possibility also stands that I'm trying to do something that I perhaps shouldn't be. Based on my research though, this guide seems to suggest using a package-wide database connection is better then creating / closing connections as needed.

Upvotes: 2

Views: 3161

Answers (1)

jeevatkm
jeevatkm

Reputation: 4791

Where should I start? Let's start with your first code snippet and make it work properly.

func init() {
    connectionString := "host=172.17.0.3 dbname=postgres user=postgres password=postgres port=5432 sslmode=disable"

    var err error
    connection, err = sql.Open(
        "postgres",
        connectionString,
    )
    check(err)

    err = connection.Ping()
    check(err)
}

In the above code snippet err is local variable and connection is package variable.


In your second code snippet err is defined as part connectionString, err := ... and you have connection package variable defined. So it works.


Using short declaration operator := defines local scoped variable. Such as:

  • If you define within func then it is function scope.
  • If you define in if then it is if else scope.

I hope it helps.

Upvotes: 8

Related Questions