Home > Net >  Golang database/sql does not connect when using env file
Golang database/sql does not connect when using env file

Time:09-30

This works perfectly and the database is connected, no problems. But I do not want to hardcode my values in the function and hence am using an env file. But using os.getenv doesn't connect to the database.

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/lib/pq"
)

const (
    host     = "localhost"
    port     = 5432
    user     = "bond"
    password = "password"
    dbname   = "bookstore"
)

func main() {

    psqlInfo := fmt.Sprintf("host=%s port=%d user=%s " 
        "password=%s dbname=%s sslmode=disable",
        host, port, user, password, dbname)

    db, err := sql.Open("postgres", psqlInfo)
    if err != nil {
        panic(err)
    }
    defer db.Close()

    err = db.Ping()
    if err != nil {
        panic(err)
    }
    fmt.Println("You connected to your database.")
}

The following code gives the error- panic: pq: password authentication failed for user "bond"

var (
    host     = "localhost"
    port     = 5432
    user     = "bond"
    password = os.Getenv("DATABASE_PWD")
    dbname   = "bookstore"
)

Why does this happen?

CodePudding user response:

Why your solution does not work as intended

I think you misunderstood what the os.GetEnv() function does, your operating system has got something that is called environment variables and that is what you are trying to get here.

Your code returns either an empty string because it is not set as an Environment variable.

You can look at the following places to learn a bit more about this function and how it works:

What you want to do

Now allow me to explain what you want to actually do, you want to get these variables from a file, so you need to:

  • tell the programs where it is
  • open it
  • parse it
  • read the values

You can do that in two ways.

First solution (Do it Yourself)

First you open the file and pass its content to a scanner. You then iterate over each line and split the line in two using the '=' character (Env format), finally store its key/value in something like a map (or whatever you want/need it to be).

Second solution (for the lazy dev or complex project)

The second, if you don't want to do all of this, just use a config library or parser do it for you.

Here is a link to do it using Viper: Load config from file & environment variables in Golang with Viper

CodePudding user response:

It appears that you are doing something like this (playground):


var (
    env      = make(map[string]string) // Simulated environoment
    password = env["DATABASE_PWD"]
)

func init() {
    // Simulate loading .env file (godotenv.Load())
    env["DATABASE_PWD"] = "foo"
}

func main() {
    fmt.Println(password) // Outputs a blank line
    fmt.Println(env["DATABASE_PWD"]) // Outputs "foo"
}

This will not work because the variables are initialized before init is run; this is as per the spec:

A package with no imports is initialized by assigning initial values to all its package-level variables followed by calling all init functions in the order they appear in the source, possibly in multiple files, as presented to the compiler. If a package has imports, the imported packages are initialized before initializing the package itself...

So the value of password is set before godotenv.Load() is run (meaning the password will be whatever value was in the environment when you started the app - probably empty).

You can fix this by loading environmental variables after you have run godotenv.Load(). I would suggest doing this in main so that you can handle any errors (godotenv.Load() may fail!). See the example in the package repo.

Note: This is why it's important to include a minimal, reproducible, example. When creating such an example you would probably have noticed that password was empty and that the issue was not at all related to database/sql. The question could not be answered without more info than provided in the question.

  • Related