func (s sqlRunner) QueryExec(query Query) error {
	if _, err := s.db.Exec(query.String(), query.args...); err != nil {
		return err
	}
	return nil
}
QueryExec used to run the query with args.
package internal
import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
	_ "github.com/go-sql-driver/mysql"
	corev1 "k8s.io/api/core/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"
	apiv1alpha1 "github.com/radondb/radondb-mysql-kubernetes/api/v1alpha1"
	"github.com/radondb/radondb-mysql-kubernetes/mysqlcluster"
	"github.com/radondb/radondb-mysql-kubernetes/utils"
)
var errorConnectionStates = []string{
	"connecting to master",
	"reconnecting after a failed binlog dump request",
	"reconnecting after a failed master event read",
	"waiting to reconnect after a failed binlog dump request",
	"waiting to reconnect after a failed master event read",
}
var internalLog = log.Log.WithName("mysql-internal")
type Config struct {
	User     string
	Password string
	Host     string
	Port     int32
}
func NewConfigFromClusterKey(c client.Client, clusterKey client.ObjectKey, userName, host string) (*Config, error) {
	cluster := &apiv1alpha1.MysqlCluster{}
	if err := c.Get(context.TODO(), clusterKey, cluster); err != nil {
		return nil, err
	}
	secret := &corev1.Secret{}
	secretKey := client.ObjectKey{Name: mysqlcluster.New(cluster).GetNameForResource(utils.Secret), Namespace: cluster.Namespace}
	if err := c.Get(context.TODO(), secretKey, secret); err != nil {
		return nil, err
	}
	if host == utils.LeaderHost {
		host = fmt.Sprintf("%s-leader.%s", cluster.Name, cluster.Namespace)
	}
	switch userName {
	case utils.OperatorUser:
		password, ok := secret.Data["operator-password"]
		if !ok {
			return nil, fmt.Errorf("operator-password cannot be empty")
		}
		return &Config{
			User:     utils.OperatorUser,
			Password: string(password),
			Host:     host,
			Port:     utils.MysqlPort,
		}, nil
	case utils.RootUser:
		password, ok := secret.Data["internal-root-password"]
		if !ok {
			return nil, fmt.Errorf("internal-root-password cannot be empty")
		}
		return &Config{
			User:     utils.RootUser,
			Password: string(password),
			Host:     host,
			Port:     utils.MysqlPort,
		}, nil
	default:
		return nil, fmt.Errorf("MySQL user %s are not supported", userName)
	}
}
func (c *Config) GetMysqlDSN() string {
	return fmt.Sprintf("%s:%s@tcp(%s:%d)/?timeout=5s&multiStatements=true&interpolateParams=true",
		c.User, c.Password, c.Host, c.Port,
	)
}
type sqlRunner struct {
	db *sql.DB
}
type SQLRunner interface {
	QueryExec(query Query) error
	QueryRow(query Query, dest ...interface{}) error
	QueryRows(query Query) (*sql.Rows, error)
}
type closeFunc func()
type SQLRunnerFactory func(cfg *Config, errs ...error) (SQLRunner, closeFunc, error)
func NewSQLRunner(cfg *Config, errs ...error) (SQLRunner, closeFunc, error) {
	var db *sql.DB
	var close closeFunc = nil
	if len(errs) > 0 && errs[0] != nil {
		return nil, close, errs[0]
	}
	db, err := sql.Open("mysql", cfg.GetMysqlDSN())
	if err != nil {
		return nil, close, err
	}
	close = func() {
		if cErr := db.Close(); cErr != nil {
			internalLog.Error(cErr, "failed closing the database connection")
		}
	}
	return &sqlRunner{db: db}, close, nil
}