【问题标题】:Does a Go Mysql driver exist that supports multiple statements within a single string?是否存在支持单个字符串中的多个语句的 Go Mysql 驱动程序?
【发布时间】:2015-04-17 09:14:19
【问题描述】:

我正在尝试找到一个 MySql 驱动程序,它可以与Go 一起使用,它支持在一次调用中发出多个 SQL 语句。例如,我可能希望使用以下 SQL 创建一个数据库:

DROP SCHEMA IF EXISTS foo;
CREATE SCHEMA IF NOT EXISTS foo;

在 PHP 等语言中,您可以将两个 SQL 语句放在一个字符串中并一次性执行,如下所示:

$db = new PDO(...);
$db->query("DROP SCHEMA IF EXISTS foo; CREATE SCHEMA IF NOT EXISTS foo;");

我需要这个的原因是因为我有 SQL 转储(来自 mysqldump)我想以编程方式应用到各种数据库。

我在 Go 中寻找相同的功能,但似乎所有不同的驱动程序都不支持它,坦率地说,这让我感到震惊。

Go-MySQL-Driver
https://github.com/go-sql-driver/mysql
这似乎是 Go 最常用的驱动程序。

package main

import "database/sql"
import "log"
import _ "github.com/go-sql-driver/mysql"

func main() {

    db, err := sql.Open("mysql", "user:password@(127.0.0.1:3306)/")
    if err != nil {
        log.Println(err)
    }

    sql := "DROP SCHEMA IF EXISTS foo; CREATE SCHEMA IF NOT EXISTS foo;"
    _, err = db.Exec(sql)
    if err != nil {
        log.Println(err)
    }

    db.Close()
}

输出:

2015/02/16 18:58:08 Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CREATE SCHEMA IF NOT EXISTS foo' at line 1

MyMySQL
https://github.com/ziutek/mymysql
这是另一个受欢迎的驱动程序。

package main

import "database/sql"
import "log"
import _ "github.com/ziutek/mymysql/godrv"

func main() {

    db, err := sql.Open("mymysql", "database/user/password")
    if err != nil {
        log.Println(err)
    }

    sql := "DROP SCHEMA IF EXISTS foo; CREATE SCHEMA IF NOT EXISTS foo;"
    _, err = db.Exec(sql)
    if err != nil {
        log.Println(err)
    }

    sql = "USE DATABASE foo;"
    _, err = db.Exec(sql) // <-- error
    if err != nil {
        log.Println(err)
    }

    db.Close()
}

输出:

2015/02/16 18:58:08 packet sequence error

有谁知道任何与 Go 兼容的 MySql 驱动程序可以处理这样的一个字符串中的多个语句?

【问题讨论】:

标签: mysql go driver


【解决方案1】:

为来自@ithkuil 的关于 go-sql-driver 包的 multiStatements 的答案添加一个示例以供参考。 (我没有足够的代表添加评论)。

multiStatements 的参数被添加到您的 sql.Open 调用的 dataSourceName 字符串中。例如

db, err := sql.Open("mysql", "user:pass@tcp(localhost:3306)/dbname?multiStatements=true")

建议您不要使用这样的 db 处理程序来处理用户输入,但它非常适合处理已知的 sql 文件。

【讨论】:

    【解决方案2】:

    github.com/go-sql-driver/mysql 可以配置为接受带有multiStatements=true 连接参数的多个语句。

    文档清楚地说明了为什么您应该小心执行此操作。 见https://github.com/go-sql-driver/mysql

    【讨论】:

    • 你知道使用多语句的语法示例代码吗?
    • 太棒了!太有帮助了llllllll
    • 实际上文档根本没有说明多语句的用法!这是全文:Allow multiple statements in one query. While this allows batch queries, it also greatly increases the risk of SQL injections. Only the result of the first query is returned, all other results are silently discarded. 没有说明应该如何执行多次插入等。
    【解决方案3】:

    我建议只需拨打 2 次电话。为什么不?它使代码更容易理解并改进了错误处理。

    另一种选择是,如果您从转储中有一个大型 SQL 文件,则可以一次性执行整个操作。

    【讨论】:

    • 进行多次调用不是一个现实的选择,因为我需要解析 SQL 转储来分解语句。可以使用 shell,但程序不是跨平台的。
    【解决方案4】:

    https://github.com/ziutek/mymysql

    可以。尽管您必须使用它的接口而不是 go 定义的接口。 go官方接口不处理,或者返回多个值。

    package main
    
    import (
        "flag"
        "fmt"
    
        "github.com/ziutek/mymysql/autorc"
        "github.com/ziutek/mymysql/mysql"
        _ "github.com/ziutek/mymysql/thrsafe"
    )
    
    type ScanFun func(int, []mysql.Row, mysql.Result) error
    
    func RunSQL(hostport, user, pass, db, cmd string, scan ScanFun) error {
        conn := autorc.New("tcp", "", hostport, user, pass, db)
    
        err := conn.Reconnect()
        if err != nil {
            return err
        }
    
        res, err := conn.Raw.Start(cmd)
        if err != nil {
            return err
        }
    
        rows, err := res.GetRows()
        if err != nil {
            return err
        }
    
        RScount := 0
        scanErr := error(nil)
    
        for {
            if scanErr == nil {
                func() {
                    defer func() {
                        if x := recover(); x != nil {
                            scanErr = fmt.Errorf("%v", x)
                        }
                    }()
                    scanErr = scan(RScount, rows, res)
                }()
            }
    
            if res.MoreResults() {
                res, err = res.NextResult()
                if err != nil {
                    return err
                }
                rows, err = res.GetRows()
                if err != nil {
                    return err
                }
            } else {
                break
            }
    
            RScount++
        }
        return scanErr
    }
    
    func main() {
        host := flag.String("host", "localhost:3306", "define the host where the db is")
        user := flag.String("user", "root", "define the user to connect as")
        pass := flag.String("pass", "", "define the pass to use")
        db := flag.String("db", "information_schema", "what db to default to")
    
        sql := flag.String("sql", "select count(*) from columns; select * from columns limit 1;", "Query to run")
    
        flag.Parse()
    
        scan := func(rcount int, rows []mysql.Row, res mysql.Result) error {
            if res.StatusOnly() {
                return nil
            }
    
            for idx, row := range rows {
                fmt.Print(rcount, "-", idx, ") ")
                for i, _ := range row {
                    fmt.Print(row.Str(i))
                    fmt.Print(" ")
                }
                fmt.Println("")
            }
            return nil
        }
    
        fmt.Println("Host - ", *host)
        fmt.Println("Db   - ", *db)
        fmt.Println("User - ", *user)
    
        if err := RunSQL(*host, *user, *pass, *db, *sql, scan); err != nil {
            fmt.Println(err)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-29
      • 1970-01-01
      • 2012-05-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-25
      相关资源
      最近更新 更多