forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/go-sql-driver/mysql/infile.go

312 lines
4.6 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// This Source Code Form is subject to the terms of the Mozilla Public
2024-02-18 10:42:21 +00:00
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
2024-02-18 10:42:21 +00:00
// You can obtain one at http://mozilla.org/MPL/2.0/.
package mysql
import (
"fmt"
"io"
"os"
"strings"
"sync"
)
var (
fileRegister map[string]bool
fileRegisterLock sync.RWMutex
readerRegister map[string]func() io.Reader
2024-02-18 10:42:21 +00:00
readerRegisterLock sync.RWMutex
)
// RegisterLocalFile adds the given file to the file allowlist,
2024-02-18 10:42:21 +00:00
// so that it can be used by "LOAD DATA LOCAL INFILE <filepath>".
2024-02-18 10:42:21 +00:00
// Alternatively you can allow the use of all local files with
2024-02-18 10:42:21 +00:00
// the DSN parameter 'allowAllFiles=true'
2024-02-18 10:42:21 +00:00
//
2024-06-14 08:41:36 +00:00
// filePath := "/home/gopher/data.csv"
2024-06-14 08:41:36 +00:00
// mysql.RegisterLocalFile(filePath)
2024-06-14 08:41:36 +00:00
// err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo")
2024-06-14 08:41:36 +00:00
// if err != nil {
2024-06-14 08:41:36 +00:00
// ...
2024-02-18 10:42:21 +00:00
func RegisterLocalFile(filePath string) {
2024-02-18 10:42:21 +00:00
fileRegisterLock.Lock()
2024-02-18 10:42:21 +00:00
// lazy map init
2024-02-18 10:42:21 +00:00
if fileRegister == nil {
2024-02-18 10:42:21 +00:00
fileRegister = make(map[string]bool)
2024-02-18 10:42:21 +00:00
}
fileRegister[strings.Trim(filePath, `"`)] = true
2024-02-18 10:42:21 +00:00
fileRegisterLock.Unlock()
2024-02-18 10:42:21 +00:00
}
// DeregisterLocalFile removes the given filepath from the allowlist.
2024-02-18 10:42:21 +00:00
func DeregisterLocalFile(filePath string) {
2024-02-18 10:42:21 +00:00
fileRegisterLock.Lock()
2024-02-18 10:42:21 +00:00
delete(fileRegister, strings.Trim(filePath, `"`))
2024-02-18 10:42:21 +00:00
fileRegisterLock.Unlock()
2024-02-18 10:42:21 +00:00
}
// RegisterReaderHandler registers a handler function which is used
2024-02-18 10:42:21 +00:00
// to receive a io.Reader.
2024-02-18 10:42:21 +00:00
// The Reader can be used by "LOAD DATA LOCAL INFILE Reader::<name>".
2024-02-18 10:42:21 +00:00
// If the handler returns a io.ReadCloser Close() is called when the
2024-02-18 10:42:21 +00:00
// request is finished.
2024-02-18 10:42:21 +00:00
//
2024-06-14 08:41:36 +00:00
// mysql.RegisterReaderHandler("data", func() io.Reader {
2024-06-14 08:41:36 +00:00
// var csvReader io.Reader // Some Reader that returns CSV data
2024-06-14 08:41:36 +00:00
// ... // Open Reader here
2024-06-14 08:41:36 +00:00
// return csvReader
2024-06-14 08:41:36 +00:00
// })
2024-06-14 08:41:36 +00:00
// err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo")
2024-06-14 08:41:36 +00:00
// if err != nil {
2024-06-14 08:41:36 +00:00
// ...
2024-02-18 10:42:21 +00:00
func RegisterReaderHandler(name string, handler func() io.Reader) {
2024-02-18 10:42:21 +00:00
readerRegisterLock.Lock()
2024-02-18 10:42:21 +00:00
// lazy map init
2024-02-18 10:42:21 +00:00
if readerRegister == nil {
2024-02-18 10:42:21 +00:00
readerRegister = make(map[string]func() io.Reader)
2024-02-18 10:42:21 +00:00
}
readerRegister[name] = handler
2024-02-18 10:42:21 +00:00
readerRegisterLock.Unlock()
2024-02-18 10:42:21 +00:00
}
// DeregisterReaderHandler removes the ReaderHandler function with
2024-02-18 10:42:21 +00:00
// the given name from the registry.
2024-02-18 10:42:21 +00:00
func DeregisterReaderHandler(name string) {
2024-02-18 10:42:21 +00:00
readerRegisterLock.Lock()
2024-02-18 10:42:21 +00:00
delete(readerRegister, name)
2024-02-18 10:42:21 +00:00
readerRegisterLock.Unlock()
2024-02-18 10:42:21 +00:00
}
func deferredClose(err *error, closer io.Closer) {
2024-02-18 10:42:21 +00:00
closeErr := closer.Close()
2024-02-18 10:42:21 +00:00
if *err == nil {
2024-02-18 10:42:21 +00:00
*err = closeErr
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
2024-02-18 10:42:21 +00:00
var rdr io.Reader
2024-02-18 10:42:21 +00:00
var data []byte
2024-02-18 10:42:21 +00:00
packetSize := 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP
2024-02-18 10:42:21 +00:00
if mc.maxWriteSize < packetSize {
2024-02-18 10:42:21 +00:00
packetSize = mc.maxWriteSize
2024-02-18 10:42:21 +00:00
}
if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader
2024-02-18 10:42:21 +00:00
// The server might return an an absolute path. See issue #355.
2024-02-18 10:42:21 +00:00
name = name[idx+8:]
readerRegisterLock.RLock()
2024-02-18 10:42:21 +00:00
handler, inMap := readerRegister[name]
2024-02-18 10:42:21 +00:00
readerRegisterLock.RUnlock()
if inMap {
2024-02-18 10:42:21 +00:00
rdr = handler()
2024-02-18 10:42:21 +00:00
if rdr != nil {
2024-02-18 10:42:21 +00:00
if cl, ok := rdr.(io.Closer); ok {
2024-02-18 10:42:21 +00:00
defer deferredClose(&err, cl)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
err = fmt.Errorf("Reader '%s' is <nil>", name)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
err = fmt.Errorf("Reader '%s' is not registered", name)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
} else { // File
2024-02-18 10:42:21 +00:00
name = strings.Trim(name, `"`)
2024-02-18 10:42:21 +00:00
fileRegisterLock.RLock()
2024-02-18 10:42:21 +00:00
fr := fileRegister[name]
2024-02-18 10:42:21 +00:00
fileRegisterLock.RUnlock()
2024-02-18 10:42:21 +00:00
if mc.cfg.AllowAllFiles || fr {
2024-02-18 10:42:21 +00:00
var file *os.File
2024-02-18 10:42:21 +00:00
var fi os.FileInfo
if file, err = os.Open(name); err == nil {
2024-02-18 10:42:21 +00:00
defer deferredClose(&err, file)
// get file size
2024-02-18 10:42:21 +00:00
if fi, err = file.Stat(); err == nil {
2024-02-18 10:42:21 +00:00
rdr = file
2024-02-18 10:42:21 +00:00
if fileSize := int(fi.Size()); fileSize < packetSize {
2024-02-18 10:42:21 +00:00
packetSize = fileSize
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
err = fmt.Errorf("local file '%s' is not registered", name)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// send content packets
2024-02-18 10:42:21 +00:00
// if packetSize == 0, the Reader contains no data
2024-02-18 10:42:21 +00:00
if err == nil && packetSize > 0 {
2024-02-18 10:42:21 +00:00
data := make([]byte, 4+packetSize)
2024-02-18 10:42:21 +00:00
var n int
2024-02-18 10:42:21 +00:00
for err == nil {
2024-02-18 10:42:21 +00:00
n, err = rdr.Read(data[4:])
2024-02-18 10:42:21 +00:00
if n > 0 {
2024-02-18 10:42:21 +00:00
if ioErr := mc.writePacket(data[:4+n]); ioErr != nil {
2024-02-18 10:42:21 +00:00
return ioErr
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err == io.EOF {
2024-02-18 10:42:21 +00:00
err = nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// send empty packet (termination)
2024-02-18 10:42:21 +00:00
if data == nil {
2024-02-18 10:42:21 +00:00
data = make([]byte, 4)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if ioErr := mc.writePacket(data[:4]); ioErr != nil {
2024-02-18 10:42:21 +00:00
return ioErr
2024-02-18 10:42:21 +00:00
}
// read OK packet
2024-02-18 10:42:21 +00:00
if err == nil {
2024-02-18 10:42:21 +00:00
return mc.readResultOK()
2024-02-18 10:42:21 +00:00
}
mc.readPacket()
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}