// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	
	
	
	
	
)

var (
	fileRegister       map[string]bool
	fileRegisterLock   sync.RWMutex
	readerRegister     map[string]func() io.Reader
	readerRegisterLock sync.RWMutex
)

// RegisterLocalFile adds the given file to the file allowlist,
// so that it can be used by "LOAD DATA LOCAL INFILE <filepath>".
// Alternatively you can allow the use of all local files with
// the DSN parameter 'allowAllFiles=true'
//
//	filePath := "/home/gopher/data.csv"
//	mysql.RegisterLocalFile(filePath)
//	err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo")
//	if err != nil {
//	...
func ( string) {
	fileRegisterLock.Lock()
	// lazy map init
	if fileRegister == nil {
		fileRegister = make(map[string]bool)
	}

	fileRegister[strings.Trim(, `"`)] = true
	fileRegisterLock.Unlock()
}

// DeregisterLocalFile removes the given filepath from the allowlist.
func ( string) {
	fileRegisterLock.Lock()
	delete(fileRegister, strings.Trim(, `"`))
	fileRegisterLock.Unlock()
}

// RegisterReaderHandler registers a handler function which is used
// to receive a io.Reader.
// The Reader can be used by "LOAD DATA LOCAL INFILE Reader::<name>".
// If the handler returns a io.ReadCloser Close() is called when the
// request is finished.
//
//	mysql.RegisterReaderHandler("data", func() io.Reader {
//		var csvReader io.Reader // Some Reader that returns CSV data
//		... // Open Reader here
//		return csvReader
//	})
//	err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo")
//	if err != nil {
//	...
func ( string,  func() io.Reader) {
	readerRegisterLock.Lock()
	// lazy map init
	if readerRegister == nil {
		readerRegister = make(map[string]func() io.Reader)
	}

	readerRegister[] = 
	readerRegisterLock.Unlock()
}

// DeregisterReaderHandler removes the ReaderHandler function with
// the given name from the registry.
func ( string) {
	readerRegisterLock.Lock()
	delete(readerRegister, )
	readerRegisterLock.Unlock()
}

func deferredClose( *error,  io.Closer) {
	 := .Close()
	if * == nil {
		* = 
	}
}

const defaultPacketSize = 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP

func ( *mysqlConn) ( string) ( error) {
	var  io.Reader
	var  []byte
	 := defaultPacketSize
	if .maxWriteSize <  {
		 = .maxWriteSize
	}

	if  := strings.Index(, "Reader::");  == 0 || ( > 0 && [-1] == '/') { // io.Reader
		// The server might return an an absolute path. See issue #355.
		 = [+8:]

		readerRegisterLock.RLock()
		,  := readerRegister[]
		readerRegisterLock.RUnlock()

		if  {
			 = ()
			if  != nil {
				if ,  := .(io.Closer);  {
					defer deferredClose(&, )
				}
			} else {
				 = fmt.Errorf("Reader '%s' is <nil>", )
			}
		} else {
			 = fmt.Errorf("Reader '%s' is not registered", )
		}
	} else { // File
		 = strings.Trim(, `"`)
		fileRegisterLock.RLock()
		 := fileRegister[]
		fileRegisterLock.RUnlock()
		if .cfg.AllowAllFiles ||  {
			var  *os.File
			var  os.FileInfo

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

				// get file size
				if ,  = .Stat();  == nil {
					 = 
					if  := int(.Size());  <  {
						 = 
					}
				}
			}
		} else {
			 = fmt.Errorf("local file '%s' is not registered", )
		}
	}

	// send content packets
	// if packetSize == 0, the Reader contains no data
	if  == nil &&  > 0 {
		 := make([]byte, 4+)
		var  int
		for  == nil {
			,  = .Read([4:])
			if  > 0 {
				if  := .writePacket([:4+]);  != nil {
					return 
				}
			}
		}
		if  == io.EOF {
			 = nil
		}
	}

	// send empty packet (termination)
	if  == nil {
		 = make([]byte, 4)
	}
	if  := .writePacket([:4]);  != nil {
		return 
	}

	// read OK packet
	if  == nil {
		return .readResultOK()
	}

	.readPacket()
	return 
}