直接从文件或字符串中执行Go中的SQL脚本

时间:2015-03-03 13:18:18

标签: postgresql go

在我的应用程序的安装脚本中,我检查数据库是否包含任何表。如果数据库为空,我有DML和DDL SQL脚本,我想运行。

从单独的.sql文件中读取SQL并不重要,所以我现在直接把它直接放到两个字符串中 - 一个用于DDL,一个用于DML - 并连接它们。

我现在的问题是,当我尝试运行脚本以生成表并使用.Exec(sqlStr)将数据插入其中时,我现在遇到此错误:

"pq: cannot insert multiple commands into a prepared statement"

我当然可以做一个解决方法。类似的东西:

sqlStr := sqlDML + sqlDDL
sqlStmtSlice := strings.Split(sqlStr, ";")
for i:= 0; i < len(sqlStmtSlice) i++ {
    // Exec() each individual statement!
}

但是,我不确定我是否喜欢这种方法。当然,必须有更好的方法从文件加载SQL脚本并执行整批,对吧?你知道吗?

聚苯乙烯。我使用PostgreSQL驱动程序用于Go,但我认为这没有任何区别。

修改

由于当时似乎没有更好的解决方案来完成这项工作,我对上面的伪代码做了一些改进,经过测试并且似乎工作得很好:

tx, err := db.Begin()

sqlStr := fmt.Sprintf(sqlDML + sqlDDL)
sqlStmtSlice := strings.Split(sqlStr, ";\r")

if err != nil {
    return err
}

defer func() {
    _ = tx.Rollback()
}()

for _, q := range sqlStmtSlice {
    _, err := tx.Exec(q)

    if err != nil {
        return err
    }
}

err = tx.Commit()

2 个答案:

答案 0 :(得分:4)

据我所知,如果不允许多个语句查询,则不是一个更好的方法。它与您正在使用的驱动程序无关,因为这是database/sql程序包限制。辩论它是否是一个好的设计是另一个问题(而且我确定已经有很多)。

在备选方面,您可以使用SQL Schema Migration工具或使用它们的灵感。一般惯例是使用语义惰性标记,例如注释,并围绕这些标记分开。

对于golang中的示例,您可以看到:

  • goose:功能齐全,可以使用Go
  • 编写迁移
  • rambler:轻量级,仅限SQL

免责声明:我是漫步者的开发者。那就是说,你应该明确地看看 goose ,这真的很酷。

答案 1 :(得分:1)

一段时间以来一直在努力解决同样的问题。我需要它的原因略有不同(在运行测试之前初始化数据库)。

正如Elwinar已经提到的,database/sql包不允许这样做。我克服这个问题的方法是创建一个python脚本(让它调用它sql_runner.py)来执行一个文件,然后运行一个go exec命令来执行python脚本(别忘了让它可执行)。

这是一个运行sql文件的python命令:cursor.execute(open("setting_up.sql"), "r").read())

运行此文件的Go代码的一部分:

cmd := exec.Command("sql.py")

cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Println(cmd.Run())