mysql.go 12.2 KiB
Newer Older
Mayuri Shinde's avatar
Mayuri Shinde committed
package mysql

import (
Roshan Patil's avatar
Roshan Patil committed
	"database/sql"
Roshan Patil's avatar
Roshan Patil committed
	"strconv"
Roshan Patil's avatar
Roshan Patil committed
	"strings"
Mayuri Shinde's avatar
Mayuri Shinde committed
	"sync"
	"time"

Roshan Patil's avatar
Roshan Patil committed
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/statemdl"

Roshan Patil's avatar
Roshan Patil committed
	_ "github.com/go-sql-driver/mysql"

	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/sjsonhelpermdl"
Mayuri Shinde's avatar
Mayuri Shinde committed

Mayuri Shinde's avatar
Mayuri Shinde committed
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/configmdl"
Mayuri Shinde's avatar
Mayuri Shinde committed
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/constantmdl"
Mayuri Shinde's avatar
Mayuri Shinde committed
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/errormdl"
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/loggermdl"
Roshan Patil's avatar
Roshan Patil committed
	"github.com/tidwall/gjson"
	"github.com/tidwall/sjson"
Mayuri Shinde's avatar
Mayuri Shinde committed

	"github.com/gocraft/dbr/v2"
Mayuri Shinde's avatar
Mayuri Shinde committed
)

// Hold a single global connection (pooling provided by sql driver)
var sqlConnections map[string]*dbr.Connection
var connectionError error
var sqlOnce sync.Once
var config tomlConfig
var defaultHost string
Mayuri Shinde's avatar
Mayuri Shinde committed

Roshan Patil's avatar
Roshan Patil committed
// MySQLConnection - MySQLConnection
type MySQLConnection struct {
	HostName        string        `json:"hostName" bson:"hostName"`
	Server          string        `json:"server" bson:"server"`
	Port            int           `json:"port" bson:"port"`
Mayuri Shinde's avatar
Mayuri Shinde committed
	Username        string        `json:"username" bson:"username"`
Roshan Patil's avatar
Roshan Patil committed
	Password        string        `json:"password" bson:"password"`
Mayuri Shinde's avatar
Mayuri Shinde committed
	Protocol        string        `json:"protocol" bson:"protocol"`
	Database        string        `json:"database" bson:"database"`
	Parameters      []param       `json:"params" bson:"params"`
Mayuri Shinde's avatar
Mayuri Shinde committed
	MaxIdleConns    int           `json:"maxIdleConns" bson:"maxIdleConns"`
	MaxOpenConns    int           `json:"maxOpenConns" bson:"maxOpenConns"`
	ConnMaxLifetime time.Duration `json:"connMaxLifetime" bson:"connMaxLifetime"`
Roshan Patil's avatar
Roshan Patil committed
	IsDefault       bool          `json:"isDefault" bson:"isDefault"`
	IsDisabled      bool          `json:"isDisabled" bson:"isDisabled"`
Roshan Patil's avatar
Roshan Patil committed
}

// InitUsingJSON - InitUsingJSON
func InitUsingJSON(configs []MySQLConnection) error {
	sqlOnce.Do(func() {
		sqlConnections = make(map[string]*dbr.Connection)

		for _, connectionDetails := range configs {
			if connectionDetails.IsDisabled {
				continue
			}
Roshan Patil's avatar
Roshan Patil committed
			connection, err := InitConnection(connectionDetails)
Roshan Patil's avatar
Roshan Patil committed
			if errormdl.CheckErr1(err) != nil {
				loggermdl.LogError("Init dbr.Open Err : ", err)
				connectionError = err
				return
			}
			pingError := connection.Ping()
			if errormdl.CheckErr1(pingError) != nil {
				loggermdl.LogError(pingError)
				connectionError = pingError
				return
			}
Roshan Patil's avatar
Roshan Patil committed
			sqlConnections[connectionDetails.HostName] = connection
			if connectionDetails.IsDefault {
				defaultHost = connectionDetails.HostName
			}
		}
	})
	return connectionError
Mayuri Shinde's avatar
Mayuri Shinde committed
}
Roshan Patil's avatar
Roshan Patil committed

Roshan Patil's avatar
Roshan Patil committed
// InitConnection - InitConnection
func InitConnection(connectionDetails MySQLConnection) (*dbr.Connection, error) {
	paramsString := strings.Builder{}

	if len(connectionDetails.Parameters) > 0 {
		for paramIndex, param := range connectionDetails.Parameters {
			if paramsString.String() == "" {
				paramsString.WriteString("?")
			}
			paramsString.WriteString(param.ParamKey)
			paramsString.WriteString("=")
			paramsString.WriteString(param.ParamValue)

			hasNextParam := paramIndex+1 < len(connectionDetails.Parameters)
			if hasNextParam {
				paramsString.WriteString("&")
			}
		}
	}
	conStr := strings.Builder{}
	conStr.WriteString(connectionDetails.Username)
	conStr.WriteString(":")
	conStr.WriteString(connectionDetails.Password)
	conStr.WriteString("@")
	conStr.WriteString(connectionDetails.Protocol)
	conStr.WriteString("(")
	conStr.WriteString(connectionDetails.Server)
	if connectionDetails.Port <= 0 || strings.TrimSpace(strconv.Itoa(connectionDetails.Port)) == "" {
		conStr.WriteString(":3306") // mysql default port is 3306
	} else {
		conStr.WriteString(":")
		conStr.WriteString(strconv.Itoa(connectionDetails.Port))
	}
	conStr.WriteString(")/")
Roshan Patil's avatar
Roshan Patil committed
	conStr.WriteString(connectionDetails.Database)
	conStr.WriteString(paramsString.String())
	connection, err := dbr.Open("mysql", conStr.String(), nil)
	if errormdl.CheckErr1(err) != nil {
		loggermdl.LogError("Init dbr.Open Err : ", err)
		return nil, err
	}
	if connectionDetails.MaxIdleConns == 0 {
		connectionDetails.MaxIdleConns = constantmdl.MAX_IDLE_CONNECTIONS // default is 2
	}
	if connectionDetails.MaxOpenConns == 0 {
		connectionDetails.MaxOpenConns = constantmdl.MAX_OPEN_CONNECTIONS // default there's no limit
	}
	if connectionDetails.ConnMaxLifetime == 0 {
		connectionDetails.ConnMaxLifetime = constantmdl.CONNECTION_MAX_LIFETIME
	}
	connection.SetMaxIdleConns(connectionDetails.MaxIdleConns)
	connection.SetMaxOpenConns(connectionDetails.MaxOpenConns)
	connection.SetConnMaxLifetime(connectionDetails.ConnMaxLifetime)
	return connection, nil
}

type param struct {
	ParamKey   string `json:"paramkey" bson:"paramkey"`
	ParamValue string `json:"paramvalue" bson:"paramvalue"`
}
Mayuri Shinde's avatar
Mayuri Shinde committed

type tomlConfig struct {
Roshan Patil's avatar
Roshan Patil committed
	MysqlHosts map[string]MySQLConnection
Mayuri Shinde's avatar
Mayuri Shinde committed
}

// Init initializes MYSQL Connections for given toml file
func Init(tomlFilepath string, defaultHostName string) (map[string]*dbr.Connection, error) {
Mayuri Shinde's avatar
Mayuri Shinde committed
	sqlOnce.Do(func() {
		sqlConnections = make(map[string]*dbr.Connection)
		_, err := configmdl.InitConfig(tomlFilepath, &config)
		if errormdl.CheckErr(err) != nil {
Mayuri Shinde's avatar
Mayuri Shinde committed
			loggermdl.LogError("Init InitConfig Err : ", err)
Mayuri Shinde's avatar
Mayuri Shinde committed
			connectionError = err
			return
		}
		for connectionName, connectionDetails := range config.MysqlHosts {
			paramsString := ""
			if len(connectionDetails.Parameters) > 0 {
				for paramIndex, param := range connectionDetails.Parameters {
					if paramsString == "" {
						paramsString = "?"
					}
					paramsString = paramsString + param.ParamKey + "=" + param.ParamValue
					hasNextParam := paramIndex+1 < len(connectionDetails.Parameters)
					if hasNextParam {
						paramsString = paramsString + "&"
					}
				}
			}
			connection, err := dbr.Open("mysql", connectionDetails.Username+":"+connectionDetails.Password+"@"+connectionDetails.Protocol+"("+connectionDetails.Server+")/"+connectionDetails.Database+paramsString, nil)
			if errormdl.CheckErr1(err) != nil {
Mayuri Shinde's avatar
Mayuri Shinde committed
				loggermdl.LogError("Init dbr.Open Err : ", err)
				connectionError = err
Mayuri Shinde's avatar
Mayuri Shinde committed
				return
			}
			if connectionDetails.MaxIdleConns == 0 {
Mayuri Shinde's avatar
Mayuri Shinde committed
				connectionDetails.MaxIdleConns = constantmdl.MAX_IDLE_CONNECTIONS // default is 2
			}
			if connectionDetails.MaxOpenConns == 0 {
Mayuri Shinde's avatar
Mayuri Shinde committed
				connectionDetails.MaxOpenConns = constantmdl.MAX_OPEN_CONNECTIONS // default there's no limit
			}
			if connectionDetails.ConnMaxLifetime == 0 {
Mayuri Shinde's avatar
Mayuri Shinde committed
				connectionDetails.ConnMaxLifetime = constantmdl.CONNECTION_MAX_LIFETIME
			}
			connection.SetMaxIdleConns(connectionDetails.MaxIdleConns)
			connection.SetMaxOpenConns(connectionDetails.MaxOpenConns)
			connection.SetConnMaxLifetime(connectionDetails.ConnMaxLifetime)
Mayuri Shinde's avatar
Mayuri Shinde committed
			sqlConnections[connectionName] = connection
		}
		defaultHost = defaultHostName
Mayuri Shinde's avatar
Mayuri Shinde committed
	})
	return sqlConnections, errormdl.CheckErr2(connectionError)
Mayuri Shinde's avatar
Mayuri Shinde committed
func GetMYSQLConnection(connectionName string) (*dbr.Connection, error) {
	if errormdl.CheckBool(sqlConnections == nil) {
Mayuri Shinde's avatar
Mayuri Shinde committed
		loggermdl.LogError("GetMYSQLConnection Err : ", errormdl.Wrap("MYSQL_INIT_NOT_DONE"))
Mayuri Shinde's avatar
Mayuri Shinde committed
		return nil, errormdl.Wrap("MYSQL_INIT_NOT_DONE")
	}
	if connectionName == "" {
		if instance, keyExist := sqlConnections[defaultHost]; keyExist {
Roshan Patil's avatar
Roshan Patil committed
			statemdl.MySQLHits()
			return instance, nil
		}
	}
	if session, keyExist := sqlConnections[connectionName]; keyExist {
Roshan Patil's avatar
Roshan Patil committed
		statemdl.MySQLHits()
Mayuri Shinde's avatar
Mayuri Shinde committed
		return session, nil
	}
Mayuri Shinde's avatar
Mayuri Shinde committed
	loggermdl.LogError("GetMYSQLConnection Err : ", errormdl.Wrap("Connection not found for host: "+connectionName))
	return nil, errormdl.Wrap("Connection not found for host: " + connectionName)
Mayuri Shinde's avatar
Mayuri Shinde committed
// MysqlDAO Mysql DAO struct
Roshan Patil's avatar
Roshan Patil committed
type MySQLDAO struct {
Mayuri Shinde's avatar
Mayuri Shinde committed
	hostName string
}

// GetMysqlDAO return Mysql DAO instance
Roshan Patil's avatar
Roshan Patil committed
func GetMySQLDAO() *MySQLDAO {
	return &MySQLDAO{
Mayuri Shinde's avatar
Mayuri Shinde committed
		hostName: defaultHost,
	}
}

// GetMysqlDAOWithHost return Mysql DAO instance
Roshan Patil's avatar
Roshan Patil committed
func GetMySQLDAOWithHost(host string) *MySQLDAO {
	return &MySQLDAO{
Mayuri Shinde's avatar
Mayuri Shinde committed
		hostName: host,
	}
}

Roshan Patil's avatar
Roshan Patil committed
// ExecQuery - ExecQuery
Roshan Patil's avatar
Roshan Patil committed
func (md *MySQLDAO) ExecQuery(query string, args ...interface{}) (string, error) {
Mayuri Shinde's avatar
Mayuri Shinde committed
	connection, connectionError := GetMYSQLConnection(md.hostName)
	if errormdl.CheckErr(connectionError) != nil {
		loggermdl.LogError("SaveUpdateOrDelete GetMYSQLConnection Err : ", connectionError)
Roshan Patil's avatar
Roshan Patil committed
		return "", errormdl.CheckErr(connectionError)
Mayuri Shinde's avatar
Mayuri Shinde committed
	}
Roshan Patil's avatar
Roshan Patil committed
	pingError := connection.Ping()
	if errormdl.CheckErr(pingError) != nil && pingError != driver.ErrBadConn {
Roshan Patil's avatar
Roshan Patil committed
		loggermdl.LogError(pingError)
Roshan Patil's avatar
Roshan Patil committed
		return "", errormdl.CheckErr(pingError)
Roshan Patil's avatar
Roshan Patil committed
	}
	result, execError := connection.Exec(query, args...)
	if errormdl.CheckErr(execError) != nil {
		loggermdl.LogError(execError)
Roshan Patil's avatar
Roshan Patil committed
		return "", errormdl.CheckErr(execError)
Roshan Patil's avatar
Roshan Patil committed
	}
Roshan Patil's avatar
Roshan Patil committed

Roshan Patil's avatar
Roshan Patil committed
	_, affectError := result.RowsAffected()
	if errormdl.CheckErr(affectError) != nil {
		loggermdl.LogError(affectError)
Roshan Patil's avatar
Roshan Patil committed
		return "", errormdl.CheckErr(affectError)
	}
	ID, err := result.LastInsertId()
	if errormdl.CheckErr(err) != nil {
		loggermdl.LogError(err)
		return "", errormdl.CheckErr(err)
Roshan Patil's avatar
Roshan Patil committed
	}
Roshan Patil's avatar
Roshan Patil committed
	return strconv.Itoa(int(ID)), nil
Mayuri Shinde's avatar
Mayuri Shinde committed
}

Roshan Patil's avatar
Roshan Patil committed
// SelectQuery - SelectQuery
func (md *MySQLDAO) SelectQuery(query string, args ...interface{}) (*gjson.Result, error) {
Mayuri Shinde's avatar
Mayuri Shinde committed
	connection, connectionError := GetMYSQLConnection(md.hostName)
	if errormdl.CheckErr(connectionError) != nil {
Roshan Patil's avatar
Roshan Patil committed
		loggermdl.LogError("SaveUpdateOrDelete GetMYSQLConnection Err : ", connectionError)
Mayuri Shinde's avatar
Mayuri Shinde committed
		return nil, errormdl.CheckErr(connectionError)
	}
Roshan Patil's avatar
Roshan Patil committed

	// loggermdl.LogSpot(connection)
	pingError := connection.Ping()
	if errormdl.CheckErr(pingError) != nil && pingError != driver.ErrBadConn {
Roshan Patil's avatar
Roshan Patil committed
		loggermdl.LogError(pingError)
		return nil, errormdl.CheckErr(pingError)
	}
	rows, queryError := connection.Query(query, args...)
	if errormdl.CheckErr(queryError) != nil {
		loggermdl.LogError(queryError)
		return nil, errormdl.CheckErr(queryError)
Mayuri Shinde's avatar
Mayuri Shinde committed
	}
	defer rows.Close()
	columns, err := rows.Columns()
	if errormdl.CheckErr2(err) != nil {
		loggermdl.LogError("GetAllData rows.Columns() Err : ", err)
		return nil, errormdl.CheckErr2(err)
	}
	values := make([]interface{}, len(columns))
	valuePtrs := make([]interface{}, len(columns))
	tableData := "[]"
	for rows.Next() {
		for i := 0; i < len(columns); i++ {
			valuePtrs[i] = &values[i]
Mayuri Shinde's avatar
Mayuri Shinde committed
		}
Mayuri Shinde's avatar
Mayuri Shinde committed
		rows.Scan(valuePtrs...)
Roshan Patil's avatar
Roshan Patil committed
		data, err := sjsonhelpermdl.SetMultiple("", columns, values)
		if errormdl.CheckErr3(err) != nil {
			loggermdl.LogError("GetAllData sjson.Set Err : ", err)
			return nil, errormdl.CheckErr3(err)
		}
		tableData, err = sjson.Set(tableData, "-1", gjson.Parse(data).Value())
		if errormdl.CheckErr3(err) != nil {
			loggermdl.LogError("GetAllData sjson.Set Err : ", err)
			return nil, errormdl.CheckErr3(err)
		}
	}
	resultSet := gjson.Parse(tableData)
	return &resultSet, nil
}

// ExecTxQuery - ExecTxQuery
Roshan Patil's avatar
Roshan Patil committed
func ExecTxQuery(query string, tx *sql.Tx, args ...interface{}) (string, error) {
Roshan Patil's avatar
Roshan Patil committed

Roshan Patil's avatar
Roshan Patil committed
	result, execError := tx.Exec(query, args...)
Roshan Patil's avatar
Roshan Patil committed
	if errormdl.CheckErr(execError) != nil {
		loggermdl.LogError(execError)
Roshan Patil's avatar
Roshan Patil committed
		return "", errormdl.CheckErr(execError)
Roshan Patil's avatar
Roshan Patil committed
	}
	_, affectError := result.RowsAffected()
	if errormdl.CheckErr(affectError) != nil {
		loggermdl.LogError(affectError)
Roshan Patil's avatar
Roshan Patil committed
		return "", errormdl.CheckErr(affectError)
	}
	ID, err := result.LastInsertId()
	if errormdl.CheckErr(err) != nil {
		loggermdl.LogError(err)
		return "", errormdl.CheckErr(err)
Roshan Patil's avatar
Roshan Patil committed
	}
Roshan Patil's avatar
Roshan Patil committed
	return strconv.Itoa(int(ID)), nil
Roshan Patil's avatar
Roshan Patil committed
}

// SelectTxQuery - SelectTxQuery
func SelectTxQuery(query string, tx *sql.Tx, args ...interface{}) (*gjson.Result, error) {
	rows, queryError := tx.Query(query, args...)
	if errormdl.CheckErr(queryError) != nil {
		loggermdl.LogError(queryError)
		return nil, errormdl.CheckErr(queryError)
	}
	defer rows.Close()
	columns, err := rows.Columns()
	if errormdl.CheckErr2(err) != nil {
		loggermdl.LogError("GetAllData rows.Columns() Err : ", err)
		return nil, errormdl.CheckErr2(err)
	}
	values := make([]interface{}, len(columns))
	valuePtrs := make([]interface{}, len(columns))
	tableData := "[]"
	for rows.Next() {
		for i := 0; i < len(columns); i++ {
			valuePtrs[i] = &values[i]
		}
		rows.Scan(valuePtrs...)
		data, err := sjsonhelpermdl.SetMultiple("", columns, values)
		if errormdl.CheckErr3(err) != nil {
			loggermdl.LogError("GetAllData sjson.Set Err : ", err)
			return nil, errormdl.CheckErr3(err)
Mayuri Shinde's avatar
Mayuri Shinde committed
		}
Mayuri Shinde's avatar
Mayuri Shinde committed
		tableData, err = sjson.Set(tableData, "-1", gjson.Parse(data).Value())
		if errormdl.CheckErr3(err) != nil {
			loggermdl.LogError("GetAllData sjson.Set Err : ", err)
			return nil, errormdl.CheckErr3(err)
		}
	}
	resultSet := gjson.Parse(tableData)
	return &resultSet, nil
}