Golang的database/sql在使用env文件时无法连接。

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

Golang database/sql does not connect when using env file

问题

这是因为在使用os.Getenv函数获取环境变量时,没有正确设置DATABASE_PWD环境变量的值。导致password变量为空,从而导致连接数据库时出现密码验证失败的错误。请确保在运行代码之前正确设置了DATABASE_PWD环境变量的值。

英文:

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?

答案1

得分: 0

为什么你的解决方案不能按预期工作

我认为你误解了os.GetEnv()函数的作用,你的操作系统有一些被称为环境变量的东西,这就是你想要获取的内容。

你的代码返回一个空字符串,因为它没有被设置为环境变量。

你可以查看以下资源来了解更多关于这个函数以及它的工作原理的信息:

你想要做什么

现在让我解释一下你实际上想要做什么,你想要从一个文件中获取这些变量,所以你需要:

  • 告诉程序文件的位置
  • 打开文件
  • 解析文件
  • 读取值

你可以用两种方式来实现。

第一种解决方案(自己动手)

首先打开文件并将其内容传递给一个扫描器。然后遍历每一行,并使用'='字符(Env格式)将行分割成两部分,最后将其键/值存储在类似于映射的数据结构中(或者任何你想要/需要的结构)。

第二种解决方案(适用于懒惰的开发者或复杂的项目)

第二种方法,如果你不想做所有这些工作,只需使用一个配置库或解析器来完成。

这里有一个使用Viper的链接来实现:
使用Viper在Golang中从文件和环境变量加载配置

英文:

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

答案2

得分: 0

看起来你正在做类似这样的事情(playground):


var (
	env      = make(map[string]string) // 模拟环境
	password = env["DATABASE_PWD"]
)

func init() {
	// 模拟加载 .env 文件(godotenv.Load())
	env["DATABASE_PWD"] = "foo"
}

func main() {
	fmt.Println(password) // 输出一个空行
	fmt.Println(env["DATABASE_PWD"]) // 输出 "foo"
}

这样是行不通的,因为变量在 init 函数运行之前就被初始化了;这是根据规范的要求:

>没有导入的包通过为其所有的包级变量分配初始值,然后按照它们在源代码中出现的顺序调用所有的 init 函数来进行初始化,可能会在多个文件中进行,这些文件按照提交给编译器的方式呈现。如果一个包有导入的包,在初始化包本身之前,导入的包会被初始化...

因此,在运行 godotenv.Load() 之前,password 的值已经被设置了(这意味着密码将是你启动应用程序时环境中的值,可能为空)。

你可以通过在运行 godotenv.Load() 之后加载环境变量来解决这个问题。我建议在 main 函数中这样做,这样你可以处理任何错误(godotenv.Load() 可能会失败!)。请参考包存储库中的示例

注意:这就是为什么包含一个最小、可重现的示例很重要。当创建这样的示例时,你可能会注意到 password 是空的,而问题与 database/sql 没有任何关系。根据提供的信息,无法回答这个问题。

英文:

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.

huangapple
  • 本文由 发表于 2022年9月29日 22:42:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/73897469.html
匿名

发表评论

匿名网友

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

确定