// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2018 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 connector struct {
	cfg               *Config // immutable private copy.
	encodedAttributes string  // Encoded connection attributes.
}

func encodeConnectionAttributes( *Config) string {
	 := make([]byte, 0)

	// default connection attributes
	 = appendLengthEncodedString(, connAttrClientName)
	 = appendLengthEncodedString(, connAttrClientNameValue)
	 = appendLengthEncodedString(, connAttrOS)
	 = appendLengthEncodedString(, connAttrOSValue)
	 = appendLengthEncodedString(, connAttrPlatform)
	 = appendLengthEncodedString(, connAttrPlatformValue)
	 = appendLengthEncodedString(, connAttrPid)
	 = appendLengthEncodedString(, strconv.Itoa(os.Getpid()))
	, ,  := net.SplitHostPort(.Addr)
	if  != "" {
		 = appendLengthEncodedString(, connAttrServerHost)
		 = appendLengthEncodedString(, )
	}

	// user-defined connection attributes
	for ,  := range strings.Split(.ConnectionAttributes, ",") {
		, ,  := strings.Cut(, ":")
		if ! {
			continue
		}
		 = appendLengthEncodedString(, )
		 = appendLengthEncodedString(, )
	}

	return string()
}

func newConnector( *Config) *connector {
	 := encodeConnectionAttributes()
	return &connector{
		cfg:               ,
		encodedAttributes: ,
	}
}

// Connect implements driver.Connector interface.
// Connect returns a connection to the database.
func ( *connector) ( context.Context) (driver.Conn, error) {
	var  error

	// Invoke beforeConnect if present, with a copy of the configuration
	 := .cfg
	if .cfg.beforeConnect != nil {
		 = .cfg.Clone()
		 = .cfg.beforeConnect(, )
		if  != nil {
			return nil, 
		}
	}

	// New mysqlConn
	 := &mysqlConn{
		maxAllowedPacket: maxPacketSize,
		maxWriteSize:     maxPacketSize - 1,
		closech:          make(chan struct{}),
		cfg:              ,
		connector:        ,
	}
	.parseTime = .cfg.ParseTime

	// Connect to Server
	dialsLock.RLock()
	,  := dials[.cfg.Net]
	dialsLock.RUnlock()
	if  {
		 := 
		if .cfg.Timeout > 0 {
			var  context.CancelFunc
			,  = context.WithTimeout(, .cfg.Timeout)
			defer ()
		}
		.netConn,  = (, .cfg.Addr)
	} else {
		 := net.Dialer{Timeout: .cfg.Timeout}
		.netConn,  = .DialContext(, .cfg.Net, .cfg.Addr)
	}
	if  != nil {
		return nil, 
	}
	.rawConn = .netConn

	// Enable TCP Keepalives on TCP connections
	if ,  := .netConn.(*net.TCPConn);  {
		if  := .SetKeepAlive(true);  != nil {
			.cfg.Logger.Print()
		}
	}

	// Call startWatcher for context support (From Go 1.8)
	.startWatcher()
	if  := .watchCancel();  != nil {
		.cleanup()
		return nil, 
	}
	defer .finish()

	.buf = newBuffer(.netConn)

	// Set I/O timeouts
	.buf.timeout = .cfg.ReadTimeout
	.writeTimeout = .cfg.WriteTimeout

	// Reading Handshake Initialization Packet
	, ,  := .readHandshakePacket()
	if  != nil {
		.cleanup()
		return nil, 
	}

	if  == "" {
		 = defaultAuthPlugin
	}

	// Send Client Authentication Packet
	,  := .auth(, )
	if  != nil {
		// try the default auth plugin, if using the requested plugin failed
		.cfg.Logger.Print("could not use requested auth plugin '"++"': ", .Error())
		 = defaultAuthPlugin
		,  = .auth(, )
		if  != nil {
			.cleanup()
			return nil, 
		}
	}
	if  = .writeHandshakeResponsePacket(, );  != nil {
		.cleanup()
		return nil, 
	}

	// Handle response to auth packet, switch methods if possible
	if  = .handleAuthResult(, );  != nil {
		// Authentication failed and MySQL has already closed the connection
		// (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
		// Do not send COM_QUIT, just cleanup and return the error.
		.cleanup()
		return nil, 
	}

	if .cfg.MaxAllowedPacket > 0 {
		.maxAllowedPacket = .cfg.MaxAllowedPacket
	} else {
		// Get max allowed packet size
		,  := .getSystemVar("max_allowed_packet")
		if  != nil {
			.Close()
			return nil, 
		}
		.maxAllowedPacket = stringToInt() - 1
	}
	if .maxAllowedPacket < maxPacketSize {
		.maxWriteSize = .maxAllowedPacket
	}

	// Handle DSN Params
	 = .handleParams()
	if  != nil {
		.Close()
		return nil, 
	}

	return , nil
}

// Driver implements driver.Connector interface.
// Driver returns &MySQLDriver{}.
func ( *connector) () driver.Driver {
	return &MySQLDriver{}
}