/****************************************************************************
** File name    :  HTDatabase.cpp
** Description  :  mysql database member function 
** Create date  :  2018.09.01
** author by    :  Liuyx
** Version info :  V1.0.01
** Copyright (C) 2002-2018 xi'an huatek, Inc Co., Ltd
** Update record:  
** DATE         AUTHER   DESC 
** -------------------------------------------------------------------------               
** 2018.09.01   Liuyx    first build
****************************************************************************/
#include "HTGlobal.h"
#include "HTDatabase.h"
static const char *_FILE_ = "HTDatabase.cpp" ;

CDBMySQL::CDBMySQL()
{
    strcpy(m_host,g_TConfig.getdbHostIP());
    strcpy(m_db,  g_TConfig.getdbName());
    strcpy(m_user,g_TConfig.getdbUserName());
    strcpy(m_password,g_TConfig.getdbUserPass());
    m_port = g_TConfig.getdbPort();
    m_min = g_TConfig.getdbMinConnect();
    mutex_create(m_csList);
}

CDBMySQL::CDBMySQL(const char *host, const char *user, const char *password, const char *db, unsigned int port/* =3306 */)
{
    strcpy(m_host, host);
    strcpy(m_user, user);
    strcpy(m_password, password);
    strcpy(m_db, db);
    m_port = port;
    m_min = g_TConfig.getdbMinConnect();
    mutex_create(m_csList);
}

CDBMySQL::~CDBMySQL()
{
    dbFreeConnectPool();
}

CDBMySQL* CDBMySQL::Instance()
{
    if(p_dbHandle == NULL)
    {
        try {
            p_dbHandle = new CDBMySQL();
        }
        catch(...) {
            vPrtLogMsg(LOG_WARNG, RET_FAIL, "CDBMySQL::Instance exceptions.");
        }
    }
    return p_dbHandle;
}
void CDBMySQL::dbFreeConnectPool()
{
    ITER_CONNECTION_HANDLE_LIST iter;
    for (iter=m_lsBusyList.begin(); iter != m_lsBusyList.end(); iter++)
    {
        mysql_close((*iter));
    }
    
    for (iter=m_lsIdleList.begin(); iter != m_lsIdleList.end(); iter++)
    {
        mysql_close((*iter));
    }
    mysql_library_end();
    mutex_close(m_csList);
}

bool CDBMySQL::dbConnectPool()
{
    try
    {
        vPrtLogMsg(LOG_DEBUG,0, "connect database:%s/%s@%s -h %s:%d start...", m_user,m_password,m_db,m_host,m_port);
        for (int i=0; i < (int)m_min; ++i)
        {
            char value = 1;
            MYSQL *pMySql = mysql_init((MYSQL*)NULL);
            //mysql_options(pMySql, MYSQL_SET_CHARSET_NAME, "gbk");
            mysql_options(pMySql, MYSQL_OPT_RECONNECT, (char *)&value);   //设置自动连接
            if (pMySql != NULL)
            {                
                if (!mysql_real_connect(pMySql,m_host,m_user,m_password,m_db,m_port,NULL,0))
                {
                    vPrtLogMsg(LOG_ERROR,errno, "connect database:%s/%s@%s -h %s:%d failed! err:%s", m_user,m_password,m_db,m_host,m_port, getLastError(pMySql));
                    return false;
                }
                mysql_set_character_set(pMySql, "GBK");
                m_lsIdleList.push_back(pMySql);
            }
            else{ 
                vPrtLogMsg(LOG_ERROR,errno, "connect database:%s/%s@%s -h %s:%d failed! err:%s", m_user,m_password,m_db,m_host,m_port, getLastError(pMySql));
            }
        }
    }
    catch (...)
    {
        vPrtLogMsg(LOG_ERROR,errno, "connect database:%s/%s@%s -h %s:%d failed!", m_user,m_password,m_db,m_host,m_port);
        return false;
    }
    return true;
}

MYSQL* CDBMySQL::GetIdleMySql()
{
    MYSQL* pMySql = NULL;

    mutex_lock(m_csList);
    if (m_lsIdleList.size() > 0)
    {
        pMySql = m_lsIdleList.front();
        if (pMySql ) {
            m_lsIdleList.pop_front();
            m_lsBusyList.push_back(pMySql);
            mysql_ping(pMySql);  // 检查连接是否正常
        }
    }
    else
    {
       pMySql = NULL;
    }
    mutex_unlock(m_csList);
    return pMySql;
}

void CDBMySQL::SetIdleMysql(MYSQL* pMySql)
{
    mutex_lock(m_csList);
    if (pMySql) {
        if (m_lsBusyList.size() > 0)
            m_lsBusyList.remove(pMySql);
         m_lsIdleList.push_back(pMySql);
    }
    mutex_unlock(m_csList);
}
//bStart = false : close auto commit
//       = true  : open auto commit
bool CDBMySQL::dbAutoCommit(MYSQL *pMysql, bool bStart)   // 开始事务处理
{
    return mysql_autocommit(pMysql, bStart) ? false : true;
}

bool CDBMySQL::dbCommit(MYSQL *pMysql)
{
    return mysql_commit(pMysql) ? false : true;
}

MYSQL_RES* CDBMySQL::SelectRecord(const char *szSql)
{
    MYSQL *pMySql = GetIdleMySql();
    if (pMySql == NULL)
    {
        return NULL;
    }
    if(mysql_query(pMySql,szSql))
    {
        vPrtLogMsg(LOG_ERROR,errno, "SelectRecord failed! SQL:[%s] err:%s", szSql, getLastError(pMySql));
        SetIdleMysql(pMySql);
        return NULL;
    }
    MYSQL_RES *myquery = NULL;
    if(!(myquery = mysql_store_result(pMySql))) {
        vPrtLogMsg(LOG_ERROR,errno, "SelectRecord failed! SQL:[%s] err:%s", szSql, getLastError(pMySql));
        myquery = NULL;
    }
    SetIdleMysql(pMySql);

    return myquery;
}


bool CDBMySQL::InsertRecordBitch(const char pszSql[][256], int count)
{
    bool bRet = false;

    MYSQL *pMySql = GetIdleMySql();
    if (pMySql == NULL)
    {
        return false;
    }
    dbAutoCommit(pMySql, false);  // 开始批量提交事务
    //mysql_query(pMySql, "START TRANSACTION"); // 开启事务， 如果没有开启事务，那么效率会变得非常低下！
    // return 0: successed  !0: failed
    for (int n = 0; n < count; n++)
    {
        if (!mysql_query(pMySql, pszSql[n]))
        {
            bRet = true;
        }
        else {
            vPrtLogMsg(LOG_ERROR, errno, "InsertRecordBitch failed! ERROR SQL[%d]:[%s] err:%s", n+1, pszSql[n], getLastError(pMySql));
        }
    }
    dbCommit(pMySql);
    dbAutoCommit(pMySql, true);  // 结束批量提交事务
    SetIdleMysql(pMySql);

    return bRet;
}


bool CDBMySQL::AddInsertRecord(MYSQL *pHandle, const char *szSql)
{
    bool bRet = false;

    // return 0: successed  !0: failed
    if (!mysql_query(pHandle, szSql))
    {
        bRet = true;
    }
    else {
        vPrtLogMsg(LOG_ERROR, errno, "AddInsertRecord failed! SQL:[%s] err:%s", szSql, getLastError(pHandle));
    }

    return bRet;
}

bool CDBMySQL::InsertRecord(const char *szSql)
{
    bool bRet = false;

    MYSQL *pMySql = GetIdleMySql();
    if (pMySql == NULL)
    {
        return false;
    }
    // return 0: successed  !0: failed
    if(!mysql_query(pMySql,szSql))
    {
        bRet = true;
    }
    else {
         vPrtLogMsg(LOG_ERROR,errno, "InsertRecord failed! SQL:[%s] err:%s", szSql, getLastError(pMySql));
    }
    SetIdleMysql(pMySql);

    return bRet;
}

bool CDBMySQL::UpdateRecord(const char *szSql)
{
    bool bRet = false;

    MYSQL *pMySql = GetIdleMySql();
    if (pMySql == NULL)
    {
        return false;
    }
    if(!mysql_query(pMySql,szSql))
    {
        bRet = true;
    }
    else {
        vPrtLogMsg(LOG_ERROR,errno, "UpdateRecord failed! SQL:[%s] err:%s", szSql, getLastError(pMySql));
    }
    SetIdleMysql(pMySql);

    return bRet;
}

bool CDBMySQL::DelRecord(const char *szSql)
{
    bool bRet = false;
    MYSQL *pMySql = GetIdleMySql();
    if (pMySql == NULL)
    {
        return false;
    }
    if(!mysql_query(pMySql,szSql))
    {
        bRet = true;
    }
    else {
        vPrtLogMsg(LOG_ERROR,errno, "DelRecord failed! SQL:[%s] err:%s", szSql, getLastError(pMySql));
    }
    SetIdleMysql(pMySql);

    return bRet;
}

bool CDBMySQL::SelectDB(const char *szDB)
{
    bool bRet = false;

    MYSQL *pMySql = GetIdleMySql();
    
    if (pMySql == NULL)  return false;

    if (mysql_select_db(pMySql,szDB)) {
        vPrtLogMsg(LOG_ERROR,errno, "SelectDB failed! dbname:[%s] err:%s", szDB, getLastError(pMySql));
        bRet = false; 
    }
    else bRet = true;

    SetIdleMysql(pMySql);

    return bRet;
}

my_ulonglong CDBMySQL::GetRowNum(MYSQL_RES *myquery)
{
    return mysql_num_rows(myquery);
}

MYSQL_ROW CDBMySQL::GetRecord(MYSQL_RES *myquery)
{
    m_row = mysql_fetch_row(myquery);

    return m_row;
}

unsigned int CDBMySQL::GetFieldNum(MYSQL_RES *myquery)
{
    return mysql_num_fields(myquery);
}

void CDBMySQL::FreeRecord(MYSQL_RES *myquery)
{
    mysql_free_result(myquery);
}

//int CDBMySQL::CreateDB(char *db)
//{
// return mysql_create_db(&m_mysql,db);
//}

void CDBMySQL::SeekData(MYSQL_RES *myquery, int offset)
{
    mysql_data_seek(myquery,offset);
}


char * CDBMySQL::getLastError(MYSQL *pMySql)
{
    return const_cast<char *>(mysql_error(pMySql));
}

bool CDBMySQL::IsEnd(MYSQL_RES *myquery)
{
    return (mysql_eof(myquery)?true:false);
}

char* CDBMySQL::GetFieldName(MYSQL_RES *myquery, int FieldNum)
{
    m_field = mysql_fetch_field_direct(myquery, FieldNum);

    return m_field->name;
}

char * CDBMySQL::GetClientInfo()
{
    return const_cast<char *>(mysql_get_client_info());
}

char* CDBMySQL::GetHostInfo()
{
    char *p = NULL;
    MYSQL *pMySql = GetIdleMySql();
    if (pMySql == NULL)
    {
        return NULL;
    }
    p = const_cast<char *>(mysql_get_host_info(pMySql));
    SetIdleMysql(pMySql);
    return p ;
}

int CDBMySQL::GetProtocolInfo()
{
    int iRet = 0;
    MYSQL *pMySql = GetIdleMySql();
    if (pMySql == NULL)
    {
        return 0;
    }
    iRet = mysql_get_proto_info(pMySql);
    SetIdleMysql(pMySql);

    return iRet;
}

char* CDBMySQL::GetServerInfo()
{
    static char szRet[1024] = {0};

    MYSQL *pMySql = GetIdleMySql();
    if (pMySql == NULL)
    {
        return NULL;
    }
    strcpy(szRet, const_cast<char *>(mysql_get_server_info(pMySql)));
    SetIdleMysql(pMySql);

    return szRet;
}

char* CDBMySQL::GetState()
{
    MYSQL *pMySql = GetIdleMySql();
    if (pMySql == NULL)     return NULL;

    static char szRet[1024] = {0} ;
    strcpy(szRet,const_cast<char *>(mysql_stat(pMySql)));
    SetIdleMysql(pMySql);

    return szRet;
}

bool CDBMySQL::SetCharset()
{
    bool bRet = false;
    //char szSql[50] = {0};
    
    //strcpy(szSql, "set names gb2312");
    MYSQL *pMySql = GetIdleMySql();
    if (pMySql == NULL)  return false;
    if(mysql_set_character_set(pMySql, "GBK"))
    //if (mysql_query(pMySql, szSql))
        bRet = true;
    SetIdleMysql(pMySql);

    return bRet;
}

//LOCK TABLES tbl1 READ, tbl2 WRITE
bool CDBMySQL::LockTable(const char *TableName, const char *Priority)
{
    bool bRet = false;
    char szSql[50] = {0} ;

    sprintf(szSql, "LOCK TABLES %s %s", TableName, Priority);
    MYSQL *pMySql = GetIdleMySql();
    if (pMySql == NULL)    return false;

    if (mysql_query(pMySql, szSql))
    bRet = true;
    SetIdleMysql(pMySql);

    return bRet;
}

bool CDBMySQL::UnlockTable()
{
    bool bRet = false;
    MYSQL *pMySql = GetIdleMySql();

    if (pMySql == NULL)    return false;

    if(mysql_query(pMySql,"UNLOCK TABLES"))  bRet = true;

    SetIdleMysql(pMySql);

    return bRet;
}

