有一个支持在单个字符串中执行多个语句的Go Mysql驱动程序吗?

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

Does a Go Mysql driver exist that supports multiple statements within a single string?

问题

我正在尝试找一个可以与Go一起使用的支持在一次调用中发出多个SQL语句的MySql驱动程序。例如,我可能希望使用以下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驱动程序可以处理像这样的一个字符串中的多个语句吗?

英文:

I'm trying to find a MySql driver that i can use with Go which supports issuing multiple SQL statements in one call. For example i might wish to create a database using the following SQL:

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

In languages such as PHP you can just place both SQL statements in one string and execute it in one go, like this:

$db = new PDO(...);
$db-&gt;query(&quot;DROP SCHEMA IF EXISTS foo; CREATE SCHEMA IF NOT EXISTS foo;&quot;);

The reason i need this is because i have SQL dumps (from mysqldump) i'd like to apply programmatically to various databases.

I'm looking for the same functionality in Go but it seems all the different drivers don't support it, which, frankly, is shocking to me.

Go-MySQL-Driver
https://github.com/go-sql-driver/mysql
This seems to be the most used driver for Go.

package main

import &quot;database/sql&quot;
import &quot;log&quot;
import _ &quot;github.com/go-sql-driver/mysql&quot;

func main() {

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

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

	db.Close()
}

output:

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 &#39;CREATE SCHEMA IF NOT EXISTS foo&#39; at line 1

MyMySQL
https://github.com/ziutek/mymysql
This is another popular driver.

package main

import &quot;database/sql&quot;
import &quot;log&quot;
import _ &quot;github.com/ziutek/mymysql/godrv&quot;

func main() {

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

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

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

	db.Close()
}

output:

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

Does anyone know of any MySql driver compatible with Go that can handle multiple statements in one string like these?

答案1

得分: 40

github.com/go-sql-driver/mysql 可以通过设置 multiStatements=true 连接参数来接受多个语句。

文档明确说明了为什么在这样做时需要小心。
请参阅 https://github.com/go-sql-driver/mysql

英文:

the github.com/go-sql-driver/mysql can be configured to accept multiple statements with the multiStatements=true connection parameter.

The documentation clearly states why you should be careful doing it.
See https://github.com/go-sql-driver/mysql

答案2

得分: 13

为了参考@ithkuil关于go-sql-driver包中multiStatements的回答,我添加了一个示例(我没有足够的声望来添加评论)。

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

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

建议您不要使用这样的数据库处理程序来处理用户输入,但对于处理已知的SQL文件非常有效。

英文:

Adding an example for the answer from @ithkuil regarding multiStatements for the go-sql-driver package for reference. (I didn't have enough rep to add as a comment).

The parameter for multiStatements is added onto the dataSourceName string for your sql.Open call. e.g.

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

It's recommended that you not use such a db handler for processing user input, but it works great for processing known sql files.

答案3

得分: 2

我建议只进行两次调用。为什么不呢?这样可以使代码更易于理解,并改善错误处理。

另一种选择是,如果你有一个来自转储的大型 SQL 文件,可以通过外壳调用一次性执行整个文件。

英文:

I would recommend simply making 2 calls. Why not? It makes the code easier to grok and improves the error handling.

The other option, if you have a large SQL file from the dump is to shell out and execute the whole thing in one go.

答案4

得分: 1

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", "定义数据库所在的主机")
	user := flag.String("user", "root", "定义要连接的用户")
	pass := flag.String("pass", "", "定义要使用的密码")
	db := flag.String("db", "information_schema", "默认使用的数据库")

	sql := flag.String("sql", "select count(*) from columns; select * from columns limit 1;", "要运行的查询")

	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)
	fmt.Println("数据库 - ", *db)
	fmt.Println("用户 - ", *user)

	if err := RunSQL(*host, *user, *pass, *db, *sql, scan); err != nil {
		fmt.Println(err)
	}
}
英文:

https://github.com/ziutek/mymysql

Can do it. Although you have to use its interface vs the go defined one. The go official interface doesn't handle it, or multiple return values.

package main
import (
&quot;flag&quot;
&quot;fmt&quot;
&quot;github.com/ziutek/mymysql/autorc&quot;
&quot;github.com/ziutek/mymysql/mysql&quot;
_ &quot;github.com/ziutek/mymysql/thrsafe&quot;
)
type ScanFun func(int, []mysql.Row, mysql.Result) error
func RunSQL(hostport, user, pass, db, cmd string, scan ScanFun) error {
conn := autorc.New(&quot;tcp&quot;, &quot;&quot;, 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(&quot;%v&quot;, 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(&quot;host&quot;, &quot;localhost:3306&quot;, &quot;define the host where the db is&quot;)
user := flag.String(&quot;user&quot;, &quot;root&quot;, &quot;define the user to connect as&quot;)
pass := flag.String(&quot;pass&quot;, &quot;&quot;, &quot;define the pass to use&quot;)
db := flag.String(&quot;db&quot;, &quot;information_schema&quot;, &quot;what db to default to&quot;)
sql := flag.String(&quot;sql&quot;, &quot;select count(*) from columns; select * from columns limit 1;&quot;, &quot;Query to run&quot;)
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, &quot;-&quot;, idx, &quot;) &quot;)
for i, _ := range row {
fmt.Print(row.Str(i))
fmt.Print(&quot; &quot;)
}
fmt.Println(&quot;&quot;)
}
return nil
}
fmt.Println(&quot;Host - &quot;, *host)
fmt.Println(&quot;Db   - &quot;, *db)
fmt.Println(&quot;User - &quot;, *user)
if err := RunSQL(*host, *user, *pass, *db, *sql, scan); err != nil {
fmt.Println(err)
}
}

huangapple
  • 本文由 发表于 2015年2月17日 03:05:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/28548559.html
匿名

发表评论

匿名网友

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

确定