【发布时间】:2016-12-24 04:59:30
【问题描述】:
我有 .sql 文件,其中包含许多数据库创建、删除、填充内容。是否有可能有一个可以执行 sql 文件的 go 函数。我使用 postgres 作为我的数据库,并为所有数据库事务使用 lib/pq 驱动程序。但是我对在我的 Go 项目中执行这个 sql 文件的任何库都是开放的。
【问题讨论】:
标签: database postgresql go
我有 .sql 文件,其中包含许多数据库创建、删除、填充内容。是否有可能有一个可以执行 sql 文件的 go 函数。我使用 postgres 作为我的数据库,并为所有数据库事务使用 lib/pq 驱动程序。但是我对在我的 Go 项目中执行这个 sql 文件的任何库都是开放的。
【问题讨论】:
标签: database postgresql go
如果你要使用命令行来执行它太麻烦了。您必须处理诸如设置密码、确保正确设置路径变量等问题。我认为最好的方法是使用数据库驱动程序并使用 Go 调用它。
在以下示例中,我将 pgx implementation of sql driver 用于 Postgres。您可以使用您选择的任何驱动程序实现来做到这一点。
path := filepath.Join("path", "to", "script.sql")
c, ioErr := ioutil.ReadFile(path)
if ioErr != nil {
// handle error.
}
sql := string(c)
_, err := *pgx.Conn.Exec(sql)
if err != nil {
// handle error.
}
解释:
【讨论】:
?multiStatements=true,否则你会得到You have an error in your SQL syntax
我在寻找类似需求时找到了 dotsql。您可以从特定文件加载命名的 sql 语句/准备语句并执行。
// Get a database handle
db, err := sql.Open("sqlite3", ":memory:")
// Loads queries from file
dot, err := dotsql.LoadFromFile("queries.sql")
// Run queries
res, err := dot.Exec(db, "create-users-table")
res, err := dot.Exec(db, "create-user", "User Name", "main@example.com")
rows, err := dot.Query(db, "find-users-by-email", "main@example.com")
row, err := dot.QueryRow(db, "find-one-user-by-email", "user@example.com")
stmt, err := dot.Prepare(db, "drop-users-table")
result, err := stmt.Exec()
【讨论】:
您可以使用标准库的os/exec 包。不需要数据库驱动程序。对于 postgreSQL,代码看起来像这样:
cmd := exec.Command("psql", "-U", psqlUser, "-h", psqlHost, "-d", psqlDBName, "-a", "-f", sqlFilePath)
var out, stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
log.Fatalf("Error executing query. Command Output: %+v\n: %+v, %v", out.String(), stderr.String(), err)
}
【讨论】:
psql 工具。
您可以将文件拆分为单独的请求并逐个执行:
file, err := ioutil.ReadFile("/some/path/to/file")
if err != nil {
// handle error
}
requests := strings.Split(string(file), ";")
for _, request := range requests {
result, err := db.Exec(request)
// do whatever you need with result and error
}
【讨论】:
requests := strings.Split(string(file), ";") 在 SQL 的情况下会中断:INSERT INTO testtable(test) VALUES('abc;def');
strings.Split(string(file), ";\n")来避免@GeneS提到的问题。
";\n" 也适用于panic: Error 1065: Query was empty 错误
使用sql驱动连接db并执行sql语句: 这是一个使用额外事务的完整示例:
func loadSQLFile(db *sqlx.DB, sqlFile string) error {
file, err := ioutil.ReadFile(sqlFile)
if err != nil {
return err
}
tx, err := db.Begin()
if err != nil {
return err
}
defer func() {
tx.Rollback()
}()
for _, q := range strings.Split(string(file), ";") {
q := strings.TrimSpace(q)
if q == "" {
continue
}
if _, err := tx.Exec(q); err != nil {
return err
}
}
return tx.Commit()
}
用法:
loadSQLFile(db, "data/my_file.sql")
执行 bash 命令的第二个选项:
func loadSQLFile(db, user, host, sqlFile string) error {
cmd := exec.Command("psql", "-U", user, "-h", host, "-d", db, "-a", "-q", "-f", sqlFile)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
用法:
loadSQLFile("my_db", "my_user", "localhost", "data/my_file.sql")
虽然第二个选项看起来更简单、更短,但我推荐第一个选项,它可以让您更好地控制自己的工作,最重要的是它更便携。
【讨论】:
这对我有用
func loadSqlFile(db *sqlx.DB) {
// Read file
file, err := ioutil.ReadFile("./test/mock.sql")
if err != nil {
fmt.Println(err.Error())
}
// Execute all
_, err = db.Exec(string(file))
if err != nil {
fmt.Println(err.Error())
}
}
【讨论】: