2016-06-03 20:33:21 +02:00
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
#include "Handle/Connection.hpp"
|
|
|
|
#include "Account.hpp"
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cstring>
|
|
|
|
#include <cstdlib>
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
namespace SqMod {
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2016-06-28 00:15:31 +02:00
|
|
|
void ConnHnd::GrabCurrent()
|
2016-06-03 20:33:21 +02:00
|
|
|
{
|
2016-06-28 00:15:31 +02:00
|
|
|
mErrNo = mysql_errno(mPtr);
|
|
|
|
mErrStr.assign(mysql_error(mPtr));
|
2016-06-03 20:33:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC)
|
2016-06-28 00:15:31 +02:00
|
|
|
void ConnHnd::ThrowCurrent(CCStr act, CCStr file, Int32 line)
|
2016-06-03 20:33:21 +02:00
|
|
|
{
|
2016-06-28 00:15:31 +02:00
|
|
|
GrabCurrent();
|
2016-06-03 20:33:21 +02:00
|
|
|
// Throw the exception with the resulted message
|
|
|
|
throw Sqrat::Exception(FmtStr("%s (%u) : %s =>[%s:%d]", act,
|
|
|
|
mErrNo, mErrStr.c_str(), file, line));
|
2016-06-28 00:15:31 +02:00
|
|
|
}
|
2016-06-03 20:33:21 +02:00
|
|
|
#else
|
2016-06-28 00:15:31 +02:00
|
|
|
void ConnHnd::ThrowCurrent(CCStr act)
|
|
|
|
{
|
|
|
|
GrabCurrent();
|
|
|
|
// Throw the exception with the resulted message
|
2016-06-03 20:33:21 +02:00
|
|
|
throw Sqrat::Exception(FmtStr("%s (%u) : %s", act,
|
|
|
|
mErrNo, mErrStr.c_str()));
|
|
|
|
}
|
2016-06-28 00:15:31 +02:00
|
|
|
#endif // _DEBUG
|
2016-06-03 20:33:21 +02:00
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2016-06-28 00:15:31 +02:00
|
|
|
ConnHnd::ConnHnd()
|
|
|
|
: mPtr(nullptr)
|
2016-06-03 20:33:21 +02:00
|
|
|
, mErrNo(0)
|
|
|
|
, mErrStr(_SC(""))
|
2016-06-28 00:15:31 +02:00
|
|
|
, mPort()
|
|
|
|
, mHost()
|
|
|
|
, mUser()
|
|
|
|
, mPass()
|
|
|
|
, mName()
|
|
|
|
, mSocket()
|
|
|
|
, mFlags()
|
|
|
|
, mSSL_Key()
|
|
|
|
, mSSL_Cert()
|
|
|
|
, mSSL_CA()
|
|
|
|
, mSSL_CA_Path()
|
|
|
|
, mSSL_Cipher()
|
2016-06-03 20:33:21 +02:00
|
|
|
, mCharset()
|
2016-06-28 00:15:31 +02:00
|
|
|
, mAutoCommit()
|
2016-06-03 20:33:21 +02:00
|
|
|
, mInTransaction(false)
|
|
|
|
{
|
2016-06-28 00:15:31 +02:00
|
|
|
/* ... */
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
ConnHnd::~ConnHnd()
|
|
|
|
{
|
|
|
|
Disconnect();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
void ConnHnd::Create(const Account & acc)
|
|
|
|
{
|
|
|
|
// Is this connection already created?
|
|
|
|
if (mPtr != nullptr)
|
|
|
|
{
|
|
|
|
STHROWF("MySQL connection was already created");
|
|
|
|
}
|
|
|
|
// Attempt to initialize a connection handle
|
|
|
|
mPtr = mysql_init(NULL);
|
2016-06-03 20:33:21 +02:00
|
|
|
// See if a connection handle could be initialized
|
|
|
|
if (!mPtr)
|
|
|
|
{
|
2016-06-28 00:15:31 +02:00
|
|
|
STHROWF("Cannot initialize MYSQL connection structure");
|
2016-06-03 20:33:21 +02:00
|
|
|
}
|
2016-06-28 00:15:31 +02:00
|
|
|
// Store all the account information
|
|
|
|
mPort = acc.GetPortNum();
|
|
|
|
mHost = acc.GetHost();
|
|
|
|
mUser = acc.GetUser();
|
|
|
|
mPass = acc.GetPass();
|
|
|
|
mName = acc.GetName();
|
|
|
|
mSocket = acc.GetSocket();
|
|
|
|
mFlags = acc.GetFlags();
|
|
|
|
mSSL_Key = acc.GetSSL_Key();
|
|
|
|
mSSL_Cert = acc.GetSSL_Cert();
|
|
|
|
mSSL_CA = acc.GetSSL_CA();
|
|
|
|
mSSL_CA_Path = acc.GetSSL_CA_Path();
|
|
|
|
mSSL_Cipher = acc.GetSSL_Cipher();
|
|
|
|
mAutoCommit = acc.GetAutoCommit();
|
2016-06-03 20:33:21 +02:00
|
|
|
// Attempt to configure SSL if specified
|
2016-06-28 00:15:31 +02:00
|
|
|
if (!mSSL_Key.empty() && mysql_ssl_set(mPtr, mSSL_Key.c_str(), mSSL_Cert.c_str(), mSSL_CA.c_str(),
|
2016-06-03 20:33:21 +02:00
|
|
|
mSSL_CA_Path.c_str(), mSSL_Cipher.c_str()) != 0)
|
|
|
|
{
|
2016-06-28 00:15:31 +02:00
|
|
|
SQMOD_THROW_CURRENT(*this, "Cannot configure SSL");
|
2016-06-03 20:33:21 +02:00
|
|
|
}
|
|
|
|
// Attempt to connect to the specified server
|
|
|
|
else if (!mysql_real_connect(mPtr, mHost.c_str(), mUser.c_str(), mPass.c_str(),
|
2016-06-28 00:15:31 +02:00
|
|
|
(mName.empty() ? nullptr : mName.c_str()), mPort,
|
|
|
|
(mSocket.empty() ? nullptr : mSocket.c_str()), mFlags))
|
2016-06-03 20:33:21 +02:00
|
|
|
{
|
2016-06-28 00:15:31 +02:00
|
|
|
SQMOD_THROW_CURRENT(*this, "Cannot connect to database");
|
2016-06-03 20:33:21 +02:00
|
|
|
}
|
|
|
|
// Attempt configure the auto-commit option
|
|
|
|
else if (mysql_autocommit(mPtr, mAutoCommit) != 0)
|
|
|
|
{
|
2016-06-28 00:15:31 +02:00
|
|
|
SQMOD_THROW_CURRENT(*this, "Cannot configure auto-commit");
|
2016-06-03 20:33:21 +02:00
|
|
|
}
|
|
|
|
// Get iterators to the options container
|
|
|
|
Account::Options::const_iterator itr = acc.GetOptions().cbegin();
|
|
|
|
Account::Options::const_iterator end = acc.GetOptions().cend();
|
|
|
|
// Process each option in the container
|
|
|
|
for (String sql(128, 0); itr != end; ++itr)
|
|
|
|
{
|
|
|
|
// Prepare the SQL query that applies the option
|
|
|
|
sql.assign("SET OPTION ");
|
|
|
|
sql.append(itr->first);
|
|
|
|
sql.append("=");
|
|
|
|
sql.append(itr->second);
|
|
|
|
// Execute the resulted query
|
|
|
|
if (Execute(sql.c_str(), static_cast< Ulong >(sql.size())) != 1)
|
|
|
|
{
|
2016-06-28 00:15:31 +02:00
|
|
|
SQMOD_THROW_CURRENT(*this, "Unable to apply option");
|
2016-06-03 20:33:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
MY_CHARSET_INFO charsetinfo;
|
|
|
|
// Grab the information about the current character set
|
|
|
|
mysql_get_character_set_info(mPtr, &charsetinfo);
|
|
|
|
// We only need the character set name
|
|
|
|
if (charsetinfo.name != nullptr)
|
|
|
|
{
|
|
|
|
mCharset.assign(charsetinfo.name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2016-06-28 00:15:31 +02:00
|
|
|
void ConnHnd::Disconnect()
|
2016-06-03 20:33:21 +02:00
|
|
|
{
|
|
|
|
if (mPtr != nullptr)
|
|
|
|
{
|
|
|
|
mysql_close(mPtr);
|
|
|
|
// mysql_init() called mysql_thread_init() therefore it needs to clear memory
|
|
|
|
// when the MYSQL handle is closed
|
|
|
|
mysql_thread_end();
|
|
|
|
// Prevent further use of this handle
|
|
|
|
mPtr = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2016-06-28 00:15:31 +02:00
|
|
|
Uint64 ConnHnd::Execute(CSStr query, Ulong size)
|
2016-06-03 20:33:21 +02:00
|
|
|
{
|
|
|
|
// Make sure that we are connected
|
|
|
|
if (!mPtr)
|
|
|
|
{
|
|
|
|
STHROWF("Invalid MySQL connection");
|
|
|
|
}
|
|
|
|
// Make sure the specified query is valid
|
|
|
|
else if (!query || *query == '\0')
|
|
|
|
{
|
|
|
|
STHROWF("Invalid or empty MySQL query");
|
|
|
|
}
|
|
|
|
// Are we supposed to compute the size?
|
|
|
|
else if (!size)
|
|
|
|
{
|
|
|
|
size = std::strlen(query);
|
|
|
|
}
|
|
|
|
// Attempt to execute the specified query
|
2016-06-28 00:15:31 +02:00
|
|
|
else if (mysql_real_query(mPtr, query, size))
|
2016-06-03 20:33:21 +02:00
|
|
|
{
|
2016-06-28 00:15:31 +02:00
|
|
|
SQMOD_THROW_CURRENT(*this, "Unable to execute query");
|
2016-06-03 20:33:21 +02:00
|
|
|
}
|
2016-06-28 00:15:31 +02:00
|
|
|
|
2016-06-03 20:33:21 +02:00
|
|
|
// Where the number of affected rows will be stored
|
2016-06-03 21:17:01 +02:00
|
|
|
Uint64 affected = 0UL;
|
2016-06-03 20:33:21 +02:00
|
|
|
// Count the number of affected rows by any "upsert" statement
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
// Attempt to retrieve a buffered result set from the executed query
|
|
|
|
ResType * result = mysql_store_result(mPtr);
|
2016-06-28 00:15:31 +02:00
|
|
|
|
2016-06-03 20:33:21 +02:00
|
|
|
// If we have a result, then this was a SELECT statement and we should not count it
|
|
|
|
// because it returns the number of selected rows and not modified/affected
|
|
|
|
if (result)
|
|
|
|
{
|
|
|
|
// Just, free the memory associated with the obtained result set
|
|
|
|
mysql_free_result(result);
|
|
|
|
}
|
|
|
|
// Non SELCT queries should have a field count of 0
|
|
|
|
else if (mysql_field_count(mPtr) == 0)
|
|
|
|
{
|
|
|
|
// Sum the number of affected rows by this statement
|
|
|
|
affected += mysql_affected_rows(mPtr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-06-28 00:15:31 +02:00
|
|
|
SQMOD_THROW_CURRENT(*this, "Unable to count affected rows");
|
2016-06-03 20:33:21 +02:00
|
|
|
}
|
|
|
|
// Prepare the next result from the executed query
|
|
|
|
// If return code is 0 then we have a result ready to process
|
|
|
|
const Int32 status = mysql_next_result(mPtr);
|
2016-06-28 00:15:31 +02:00
|
|
|
|
2016-06-03 20:33:21 +02:00
|
|
|
// If return code is higher than 0 then an error occurred
|
|
|
|
if (status > 0)
|
|
|
|
{
|
2016-06-28 00:15:31 +02:00
|
|
|
SQMOD_THROW_CURRENT(*this, "Unable to prepare next result");
|
2016-06-03 20:33:21 +02:00
|
|
|
}
|
|
|
|
// If return code is less than 0 then there are no results left
|
|
|
|
else if (status < 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Return the number of affected rows
|
|
|
|
return affected;
|
|
|
|
}
|
|
|
|
|
2016-06-28 00:15:31 +02:00
|
|
|
} // Namespace:: SqMod
|