今天来更新一个很少碰到,但碰到了又让人十分蛋疼的问题——Go语言中执行MySQL的load data local infile语句报local file 'xxx' is not registered错误该如何解决。

上车请刷卡,没卡的乘客请投币,上车的乘客请往车厢中部走,汽车起步,请坐稳扶好。

情景在现:我要在Go语言中执行sql语句,往MySQL中导入一个.csv文件,sql语句如下:

load data LOCAL infile './user.csv' into table users
CHARACTER SET utf8 fields terminated by ',' lines terminated by '\r\n' ignore 1 lines(age,name);
1
2
数据库驱动用的是"github.com/go-sql-driver/mysql",go语言代码如下:

package main

import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)

var (
db *sql.DB
dburl = "root:******@/test?charset=utf8&parseTime=True&loc=Local"
sql = `load data LOCAL infile 'F://user.csv' into table users CHARACTER SET utf8 fields terminated by ',' lines terminated by '\r\n' ignore 1 lines(age,name);`
)

func init() {
var err error
db, err = sql.Open("mysql", dburl)
if err != nil {
panic(err)
}
}

func main() {
_, err := db.Exec(sql)
if err != nil {
fmt.Println(err)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[注:以上代码不能直接食用,除非你装了MySQL]

一切看起来都很美好,然而运行的时候。。。会报错下面的错误。

local file 'F://user.csv' is not registered
1

[龙叔对不住啦:手动捂脸]

什么鬼?文件未注册?文件还需要注册?明明在cmd里面执行这句sql就没有问题,偏偏在go里面就不行,而且怎么弄都不行,简直想砸电。。“电脑太贵了”。。简直想砸键盘,然而。。。。砸完键盘之后,问题还是要解决的。

经过一番挖地三尺的搜索,终于找到了问题的根源所在。

在MySQL驱动的infile.go文件中,有一个叫handleInFileRequest的函数,其中有这样一段代码:

package mysql

func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
...
fr := fileRegister[name] // 这里的 name 就是文件名:F://user.csv
if mc.cfg.AllowAllFiles || fr {
var file *os.File
var fi os.FileInfo

if file, err = os.Open(name); err == nil {
defer deferredClose(&err, file)

// get file size
if fi, err = file.Stat(); err == nil {
rdr = file
if fileSize := int(fi.Size()); fileSize < packetSize {
packetSize = fileSize
}
}
}
} else {
err = fmt.Errorf("local file '%s' is not registered", name) //<--根源所在
}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
错误就来自这段代码,问题很明显了,mc.cfg.AllowAllFiles = false并且fr为空。因此我们可以从两个方面来解决这个问题。

方案一、解决fr为空的问题。fileRegister实际上是一个map,在infile.go中定义,并且有一个函数作为接口:

package mysql

var (
fileRegister map[string]bool
)

func RegisterLocalFile(filePath string) {
fileRegisterLock.Lock()
// lazy map init
if fileRegister == nil {
fileRegister = make(map[string]bool)
}

fileRegister[strings.Trim(filePath, `"`)] = true
fileRegisterLock.Unlock()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
RegisterLocalFile是一个导出函数,可以在我们的main函数中调用,但前提是不能以_的方式导入驱动包github.com/go-sql-driver/mysql。将代码修改如下:

package main

import (
"database/sql"
"fmt"
"github.com/go-sql-driver/mysql"
)

var (
db *sql.DB
dburl = "root:******@/test?charset=utf8&parseTime=True&loc=Local"
sql = `load data LOCAL infile 'F://user.csv' into table users CHARACTER SET utf8 fields terminated by ',' lines terminated by '\r\n' ignore 1 lines(age,name);`
)

func init() {
var err error
db, err = sql.Open("mysql", dburl)
if err != nil {
panic(err)
}
}

func main() {
mysql.RegisterLocalFile("F://user.csv")
_, err := db.Exec(sql)
if err != nil {
fmt.Println(err)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
运行成功。需要注意的是,sql语句中的文件名和RegisterLocalFile带入的文件名必须完全一样,而且是绝对路径。

方案二、我们还可以通过修改mc.cfg.AllowAllFiles的值来解决这个问题。handleInFileRequest是mysqlConn的函数,我们需要看看mysqlConn的定义。

type mysqlConn struct {
...
cfg *Config
...
}

1
2
3
4
5
6
去掉那些无关紧要的字段,我们看到cfg字段是*Config类型,再看看Config结构体的定义。

type Config struct {
...
AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
...
}
1
2
3
4
5
看到这里是不是很开心呢?不过不要开心的太早了,虽然AllowAllFiles是导出的,但是cfg是未导出字段,并且mysqlConn并没有提供有关cfg的接口,玩个蛋。。。

就在我以为此路不通而心灰意冷自怨自艾濒临放弃怨天尤人的时候,天无绝人之路。

在dsn.go中,我发现了这样一个函数:

package mysql

func parseDSNParams(cfg *Config, params string) (err error) {
for _, v := range strings.Split(params, "&") {
param := strings.SplitN(v, "=", 2)
if len(param) != 2 {
continue
}

// cfg params
switch value := param[1]; param[0] {
// Disable INFILE whitelist / enable all files
case "allowAllFiles":
var isBool bool
cfg.AllowAllFiles, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
...
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
还记得sql.Open函数吗?他的第二个参数需要一个字符串来指定用户名,密码,表名等等信息。parseDSNParams函数就是在解析这个字符串,并且设置相应的参数。所以我们只需要在原来的dburl最后加上&allowAllFiles=true就可以了。

dburl = "root:******@/test?charset=utf8&parseTime=True&loc=Local&allowAllFiles=true"
1
其他都不用改,再次运行,成功。
---------------------

用Go向MySQL导入.csv文件的更多相关文章

  1. Mysql 导入CSV文件,中文内容乱码问题

    项目中用到含有中文字段的数据CSV文件,导入Mysql数据中发现中文内容乱码. 分析原因:因为数据库字符编码问题引起. [1]创建utf-8字符集数据库 CREATE DATABASE db_name ...

  2. mysql导入csv文件

    今天尝试将Oracle中的数据导入到mysql中,在SQLyog工具其中看到一些sql语句,拿来记录一下,说不定以后就用的着呐! -----查看ydtf数据库中的基础表,就是用户创建了哪些表 SHOW ...

  3. MySQL导入csv文件内容到Table及数据库的自增主键设置

    写在前面 目的是测试将csv文件内容导入到表中, 同时记录一下自增主键的设置. 测试采用MySQL8.0. 新建表customer_info如下, 未设置主键. 修改上表, 添加主键id, 并设置为自 ...

  4. mysql导入.csv文件出错

    1.报错信息 ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cann ...

  5. mysql 导入 CSV文件命令行 ERROR 13 (HY000): Can't get stat of

    一定要查看好CSV字段结构是否和文件的表结构字段一致 load data local infile 'F:/MySqlData/test1.csv' --CSV文件存放路径 into table st ...

  6. mysql 导入 csv文件中数据,只能导入第一行

    用workbench导入csv数据,只能导入数据的第一行,也就是标注每一列的列名的那一行.但问题是,每次导入完成时,系统提示已经导入了500条记录(这个文件中的确有500条记录),可是刷新数据库后打开 ...

  7. mysql 导入csv文件

    导入时,系统会默认一个导入路径,如果导入路径不是默认路径,会报 The MySQL server is running with the --secure-file-priv option so it ...

  8. MYSQL导入CSV格式文件数据执行提示错误(ERROR 1290): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement.

    MYSQL导入CSV格式文件数据执行提示错误(ERROR 1290): The MySQL server is running with the --secure-file-priv option s ...

  9. mysql导出csv文件excel打开后数字用科学计数法显示且低位变0的解决方法

    Excel显示数字时,如果数字大于12位,它会自动转化为科学计数法:如果数字大于15位,它不仅用于科学技术费表示,还会只保留高15位,其他位都变0. Excel打开csv文件时,只要字段值都是数字,它 ...

随机推荐

  1. Test redis

    单机测试: public class RedisClient{ private Jedis jedis; private JedisPool jedisPool; private ShardedJed ...

  2. POJ2289 Jamie's Contact Groups —— 二分图多重匹配/最大流 + 二分

    题目链接:https://vjudge.net/problem/POJ-2289 Jamie's Contact Groups Time Limit: 7000MS   Memory Limit: 6 ...

  3. 以太坊客户端geth的基本操作命令

    以太坊客户端geth的基本操作命令搭建了私有链环境之后,整理了一下客户端的一些基本的操作命令: 启动命令重复上篇博客步骤,先将区块链客户端启动,命令如下: geth –datadir “%cd%\ch ...

  4. 修改eclipse启动程序超时时间

    修改workspace\.metadata\.plugins\org.eclipse.wst.server.core\servers.xml 把其中的start-timeout="45&qu ...

  5. Servlet单例非安全解析

    Servlet容器默认是采用单实例多线程的方式处理多个请求 Servlet容器<Web容器<应用服务器?apache<tomcat<websphere Servlet不是线程安 ...

  6. 【Usaco2008 Dec】Patting Heads

    [题目链接] 点击打开链接 [算法] 我们知道,每个编号为a[i]都要被编号是a[i]的约数的牛拍一次头(除了它自己),因此,只需用类似于筛法的方式统计答案, 即可 [代码] #include< ...

  7. 【IOI 1996】 Network of Schools

    [题目链接] 点击打开链接 [算法] 对于第一问,将这个图缩点,输出出度为零的点的个数 对于第二问,同样将这个图缩点,输出入度为零.出度为零的点的个数的最大值 [代码] #include <al ...

  8. codeforces round 418 div2 补题 CF 814 A-E

    A An abandoned sentiment from past 水题 #include<bits/stdc++.h> using namespace std; int a[300], ...

  9. appium学习【三】:截图时,图片命令中包含当前的函数名,以区分错误是在哪个函数报的

    import sys funcName = sys._getframe().f_back.f_code.co_name #获取调用函数名 print sys._getframe().f_code.co ...

  10. 转载:百为STM32开发板教程之十二——NAND FLASH

    http://bbs.21ic.com/icview-586200-1-1.html 百为STM32开发板教程之十二——NAND FLASH 参考资料:百为stm32开发板光盘V3\百为stm32开发 ...