// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2012 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 (
	
	
	
	
	
	
	
	
	
)

type mysqlConn struct {
	buf              buffer
	netConn          net.Conn
	rawConn          net.Conn // underlying connection when netConn is TLS connection.
	affectedRows     uint64
	insertId         uint64
	cfg              *Config
	maxAllowedPacket int
	maxWriteSize     int
	writeTimeout     time.Duration
	flags            clientFlag
	status           statusFlag
	sequence         uint8
	parseTime        bool
	reset            bool // set when the Go SQL package calls ResetSession

	// for context support (Go 1.8+)
	watching bool
	watcher  chan<- context.Context
	closech  chan struct{}
	finished chan<- struct{}
	canceled atomicError // set non-nil if conn is canceled
	closed   atomicBool  // set when conn is closed, before closech is closed
}

// Handles parameters set in DSN after the connection is established
func ( *mysqlConn) () ( error) {
	var  strings.Builder
	for ,  := range .cfg.Params {
		switch  {
		// Charset: character_set_connection, character_set_client, character_set_results
		case "charset":
			 := strings.Split(, ",")
			for  := range  {
				// ignore errors here - a charset may not exist
				 = .exec("SET NAMES " + [])
				if  == nil {
					break
				}
			}
			if  != nil {
				return
			}

		// Other system vars accumulated in a single SET command
		default:
			if .Len() == 0 {
				// Heuristic: 29 chars for each other key=value to reduce reallocations
				.Grow(4 + len() + 1 + len() + 30*(len(.cfg.Params)-1))
				.WriteString("SET ")
			} else {
				.WriteString(", ")
			}
			.WriteString()
			.WriteString(" = ")
			.WriteString()
		}
	}

	if .Len() > 0 {
		 = .exec(.String())
		if  != nil {
			return
		}
	}

	return
}

func ( *mysqlConn) ( error) error {
	if  == nil {
		return 
	}
	if  != errBadConnNoWrite {
		return 
	}
	return driver.ErrBadConn
}

func ( *mysqlConn) () (driver.Tx, error) {
	return .begin(false)
}

func ( *mysqlConn) ( bool) (driver.Tx, error) {
	if .closed.Load() {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	var  string
	if  {
		 = "START TRANSACTION READ ONLY"
	} else {
		 = "START TRANSACTION"
	}
	 := .exec()
	if  == nil {
		return &mysqlTx{}, 
	}
	return nil, .markBadConn()
}

func ( *mysqlConn) () ( error) {
	// Makes Close idempotent
	if !.closed.Load() {
		 = .writeCommandPacket(comQuit)
	}

	.cleanup()

	return
}

// Closes the network connection and unsets internal variables. Do not call this
// function after successfully authentication, call Close instead. This function
// is called before auth or on auth failure because MySQL will have already
// closed the network connection.
func ( *mysqlConn) () {
	if .closed.Swap(true) {
		return
	}

	// Makes cleanup idempotent
	close(.closech)
	if .netConn == nil {
		return
	}
	if  := .netConn.Close();  != nil {
		errLog.Print()
	}
}

func ( *mysqlConn) () error {
	if .closed.Load() {
		if  := .canceled.Value();  != nil {
			return 
		}
		return ErrInvalidConn
	}
	return nil
}

func ( *mysqlConn) ( string) (driver.Stmt, error) {
	if .closed.Load() {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	// Send command
	 := .writeCommandPacketStr(comStmtPrepare, )
	if  != nil {
		// STMT_PREPARE is safe to retry.  So we can return ErrBadConn here.
		errLog.Print()
		return nil, driver.ErrBadConn
	}

	 := &mysqlStmt{
		mc: ,
	}

	// Read Result
	,  := .readPrepareResultPacket()
	if  == nil {
		if .paramCount > 0 {
			if  = .readUntilEOF();  != nil {
				return nil, 
			}
		}

		if  > 0 {
			 = .readUntilEOF()
		}
	}

	return , 
}

func ( *mysqlConn) ( string,  []driver.Value) (string, error) {
	// Number of ? should be same to len(args)
	if strings.Count(, "?") != len() {
		return "", driver.ErrSkip
	}

	,  := .buf.takeCompleteBuffer()
	if  != nil {
		// can not take the buffer. Something must be wrong with the connection
		errLog.Print()
		return "", ErrInvalidConn
	}
	 = [:0]
	 := 0

	for  := 0;  < len(); ++ {
		 := strings.IndexByte([:], '?')
		if  == -1 {
			 = append(, [:]...)
			break
		}
		 = append(, [:+]...)
		 += 

		 := []
		++

		if  == nil {
			 = append(, "NULL"...)
			continue
		}

		switch v := .(type) {
		case int64:
			 = strconv.AppendInt(, , 10)
		case uint64:
			// Handle uint64 explicitly because our custom ConvertValue emits unsigned values
			 = strconv.AppendUint(, , 10)
		case float64:
			 = strconv.AppendFloat(, , 'g', -1, 64)
		case bool:
			if  {
				 = append(, '1')
			} else {
				 = append(, '0')
			}
		case time.Time:
			if .IsZero() {
				 = append(, "'0000-00-00'"...)
			} else {
				 = append(, '\'')
				,  = appendDateTime(, .In(.cfg.Loc))
				if  != nil {
					return "", 
				}
				 = append(, '\'')
			}
		case json.RawMessage:
			 = append(, '\'')
			if .status&statusNoBackslashEscapes == 0 {
				 = escapeBytesBackslash(, )
			} else {
				 = escapeBytesQuotes(, )
			}
			 = append(, '\'')
		case []byte:
			if  == nil {
				 = append(, "NULL"...)
			} else {
				 = append(, "_binary'"...)
				if .status&statusNoBackslashEscapes == 0 {
					 = escapeBytesBackslash(, )
				} else {
					 = escapeBytesQuotes(, )
				}
				 = append(, '\'')
			}
		case string:
			 = append(, '\'')
			if .status&statusNoBackslashEscapes == 0 {
				 = escapeStringBackslash(, )
			} else {
				 = escapeStringQuotes(, )
			}
			 = append(, '\'')
		default:
			return "", driver.ErrSkip
		}

		if len()+4 > .maxAllowedPacket {
			return "", driver.ErrSkip
		}
	}
	if  != len() {
		return "", driver.ErrSkip
	}
	return string(), nil
}

func ( *mysqlConn) ( string,  []driver.Value) (driver.Result, error) {
	if .closed.Load() {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	if len() != 0 {
		if !.cfg.InterpolateParams {
			return nil, driver.ErrSkip
		}
		// try to interpolate the parameters to save extra roundtrips for preparing and closing a statement
		,  := .interpolateParams(, )
		if  != nil {
			return nil, 
		}
		 = 
	}
	.affectedRows = 0
	.insertId = 0

	 := .exec()
	if  == nil {
		return &mysqlResult{
			affectedRows: int64(.affectedRows),
			insertId:     int64(.insertId),
		}, 
	}
	return nil, .markBadConn()
}

// Internal function to execute commands
func ( *mysqlConn) ( string) error {
	// Send command
	if  := .writeCommandPacketStr(comQuery, );  != nil {
		return .markBadConn()
	}

	// Read Result
	,  := .readResultSetHeaderPacket()
	if  != nil {
		return 
	}

	if  > 0 {
		// columns
		if  := .readUntilEOF();  != nil {
			return 
		}

		// rows
		if  := .readUntilEOF();  != nil {
			return 
		}
	}

	return .discardResults()
}

func ( *mysqlConn) ( string,  []driver.Value) (driver.Rows, error) {
	return .query(, )
}

func ( *mysqlConn) ( string,  []driver.Value) (*textRows, error) {
	if .closed.Load() {
		errLog.Print(ErrInvalidConn)
		return nil, driver.ErrBadConn
	}
	if len() != 0 {
		if !.cfg.InterpolateParams {
			return nil, driver.ErrSkip
		}
		// try client-side prepare to reduce roundtrip
		,  := .interpolateParams(, )
		if  != nil {
			return nil, 
		}
		 = 
	}
	// Send command
	 := .writeCommandPacketStr(comQuery, )
	if  == nil {
		// Read Result
		var  int
		,  = .readResultSetHeaderPacket()
		if  == nil {
			 := new(textRows)
			.mc = 

			if  == 0 {
				.rs.done = true

				switch  := .NextResultSet();  {
				case nil, io.EOF:
					return , nil
				default:
					return nil, 
				}
			}

			// Columns
			.rs.columns,  = .readColumns()
			return , 
		}
	}
	return nil, .markBadConn()
}

// Gets the value of the given MySQL System Variable
// The returned byte slice is only valid until the next read
func ( *mysqlConn) ( string) ([]byte, error) {
	// Send command
	if  := .writeCommandPacketStr(comQuery, "SELECT @@"+);  != nil {
		return nil, 
	}

	// Read Result
	,  := .readResultSetHeaderPacket()
	if  == nil {
		 := new(textRows)
		.mc = 
		.rs.columns = []mysqlField{{fieldType: fieldTypeVarChar}}

		if  > 0 {
			// Columns
			if  := .readUntilEOF();  != nil {
				return nil, 
			}
		}

		 := make([]driver.Value, )
		if  = .readRow();  == nil {
			return [0].([]byte), .readUntilEOF()
		}
	}
	return nil, 
}

// finish is called when the query has canceled.
func ( *mysqlConn) ( error) {
	.canceled.Set()
	.cleanup()
}

// finish is called when the query has succeeded.
func ( *mysqlConn) () {
	if !.watching || .finished == nil {
		return
	}
	select {
	case .finished <- struct{}{}:
		.watching = false
	case <-.closech:
	}
}

// Ping implements driver.Pinger interface
func ( *mysqlConn) ( context.Context) ( error) {
	if .closed.Load() {
		errLog.Print(ErrInvalidConn)
		return driver.ErrBadConn
	}

	if  = .watchCancel();  != nil {
		return
	}
	defer .finish()

	if  = .writeCommandPacket(comPing);  != nil {
		return .markBadConn()
	}

	return .readResultOK()
}

// BeginTx implements driver.ConnBeginTx interface
func ( *mysqlConn) ( context.Context,  driver.TxOptions) (driver.Tx, error) {
	if .closed.Load() {
		return nil, driver.ErrBadConn
	}

	if  := .watchCancel();  != nil {
		return nil, 
	}
	defer .finish()

	if sql.IsolationLevel(.Isolation) != sql.LevelDefault {
		,  := mapIsolationLevel(.Isolation)
		if  != nil {
			return nil, 
		}
		 = .exec("SET TRANSACTION ISOLATION LEVEL " + )
		if  != nil {
			return nil, 
		}
	}

	return .begin(.ReadOnly)
}

func ( *mysqlConn) ( context.Context,  string,  []driver.NamedValue) (driver.Rows, error) {
	,  := namedValueToValue()
	if  != nil {
		return nil, 
	}

	if  := .watchCancel();  != nil {
		return nil, 
	}

	,  := .query(, )
	if  != nil {
		.finish()
		return nil, 
	}
	.finish = .finish
	return , 
}

func ( *mysqlConn) ( context.Context,  string,  []driver.NamedValue) (driver.Result, error) {
	,  := namedValueToValue()
	if  != nil {
		return nil, 
	}

	if  := .watchCancel();  != nil {
		return nil, 
	}
	defer .finish()

	return .Exec(, )
}

func ( *mysqlConn) ( context.Context,  string) (driver.Stmt, error) {
	if  := .watchCancel();  != nil {
		return nil, 
	}

	,  := .Prepare()
	.finish()
	if  != nil {
		return nil, 
	}

	select {
	default:
	case <-.Done():
		.Close()
		return nil, .Err()
	}
	return , nil
}

func ( *mysqlStmt) ( context.Context,  []driver.NamedValue) (driver.Rows, error) {
	,  := namedValueToValue()
	if  != nil {
		return nil, 
	}

	if  := .mc.watchCancel();  != nil {
		return nil, 
	}

	,  := .query()
	if  != nil {
		.mc.finish()
		return nil, 
	}
	.finish = .mc.finish
	return , 
}

func ( *mysqlStmt) ( context.Context,  []driver.NamedValue) (driver.Result, error) {
	,  := namedValueToValue()
	if  != nil {
		return nil, 
	}

	if  := .mc.watchCancel();  != nil {
		return nil, 
	}
	defer .mc.finish()

	return .Exec()
}

func ( *mysqlConn) ( context.Context) error {
	if .watching {
		// Reach here if canceled,
		// so the connection is already invalid
		.cleanup()
		return nil
	}
	// When ctx is already cancelled, don't watch it.
	if  := .Err();  != nil {
		return 
	}
	// When ctx is not cancellable, don't watch it.
	if .Done() == nil {
		return nil
	}
	// When watcher is not alive, can't watch it.
	if .watcher == nil {
		return nil
	}

	.watching = true
	.watcher <- 
	return nil
}

func ( *mysqlConn) () {
	 := make(chan context.Context, 1)
	.watcher = 
	 := make(chan struct{})
	.finished = 
	go func() {
		for {
			var  context.Context
			select {
			case  = <-:
			case <-.closech:
				return
			}

			select {
			case <-.Done():
				.cancel(.Err())
			case <-:
			case <-.closech:
				return
			}
		}
	}()
}

func ( *mysqlConn) ( *driver.NamedValue) ( error) {
	.Value,  = converter{}.ConvertValue(.Value)
	return
}

// ResetSession implements driver.SessionResetter.
// (From Go 1.10)
func ( *mysqlConn) ( context.Context) error {
	if .closed.Load() {
		return driver.ErrBadConn
	}
	.reset = true
	return nil
}

// IsValid implements driver.Validator interface
// (From Go 1.15)
func ( *mysqlConn) () bool {
	return !.closed.Load()
}