////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include
#include "database.h"
#include "databasepgsql.h"
#include "configmanager.h"
extern ConfigManager g_config;
DatabasePgSQL::DatabasePgSQL()
{
std::stringstream dns;
dns << "host='" << g_config.getString(ConfigManager::SQL_HOST) << "' dbname='" << g_config.getString(ConfigManager::SQL_DB) << "' user='" << g_config.getString(ConfigManager::SQL_USER) << "' password='" << g_config.getString(ConfigManager::SQL_PASS) << "' port='" << g_config.getNumber(ConfigManager::SQL_PORT) << "'";
m_handle = PQconnectdb(dns.str().c_str());
m_connected = PQstatus(m_handle) == CONNECTION_OK;
if(!m_connected)
std::cout << "Failed to estabilish PostgreSQL database connection: " << PQerrorMessage(m_handle) << std::endl;
}
bool DatabasePgSQL::getParam(DBParam_t param)
{
switch(param)
{
case DBPARAM_MULTIINSERT:
return true;
default:
break;
}
return false;
}
bool DatabasePgSQL::executeQuery(const std::string& query)
{
if(!m_connected)
return false;
#ifdef __SQL_QUERY_DEBUG__
std::cout << "PGSQL QUERY: " << query << std::endl;
#endif
// executes query
PGresult* res = PQexec(m_handle, _parse(query).c_str());
ExecStatusType stat = PQresultStatus(res);
if(stat != PGRES_COMMAND_OK && stat != PGRES_TUPLES_OK)
{
std::cout << "PQexec(): " << query << ": " << PQresultErrorMessage(res) << std::endl;
PQclear(res);
return false;
}
// everything went fine
PQclear(res);
return true;
}
DBResult* DatabasePgSQL::storeQuery(const std::string& query)
{
if(!m_connected)
return NULL;
#ifdef __SQL_QUERY_DEBUG__
std::cout << "PGSQL QUERY: " << query << std::endl;
#endif
// executes query
PGresult* res = PQexec(m_handle, _parse(query).c_str());
ExecStatusType stat = PQresultStatus(res);
if(stat != PGRES_COMMAND_OK && stat != PGRES_TUPLES_OK)
{
std::cout << "PQexec(): " << query << ": " << PQresultErrorMessage(res) << std::endl;
PQclear(res);
return false;
}
// everything went fine
DBResult* results = new PgSQLResult(res);
return verifyResult(results);
}
std::string DatabasePgSQL::escapeString(const std::string& s)
{
// remember to quote even empty string!
if(!s.size())
return std::string("''");
// the worst case is 2n + 1
int32_t error;
char* output = new char[(s.length() * 2) + 1];
// quotes escaped string and frees temporary buffer
PQescapeStringConn(m_handle, output, s.c_str(), s.length(), reinterpret_cast(&error));
std::string r = std::string("'");
r += output;
r += "'";
delete[] output;
return r;
}
std::string DatabasePgSQL::escapeBlob(const char *s, uint32_t length)
{
// remember to quote even empty stream!
if(!s)
return std::string("''");
// quotes escaped string and frees temporary buffer
size_t len;
char* output = (char*)PQescapeByteaConn(m_handle, (uint8_t*)s, length, &len);
std::string r = std::string("E'");
r += output;
r += "'";
PQfreemem(output);
return r;
}
uint64_t DatabasePgSQL::getLastInsertId()
{
if(!m_connected)
return 0;
PGresult* res = PQexec(m_handle, "SELECT LASTVAL() as last;");
ExecStatusType stat = PQresultStatus(res);
if(stat != PGRES_COMMAND_OK && stat != PGRES_TUPLES_OK)
{
std::cout << "PQexec(): " << query << ": " << PQresultErrorMessage(res) << std::endl;
PQclear(res);
return 0;
}
const uint64_t id = atoll(PQgetvalue(res, 0, PQfnumber(res, "last")));
PGClear(res);
return id;
}
std::string DatabasePgSQL::_parse(const std::string& s)
{
std::string query = "";
bool inString = false;
for(uint32_t a = 0; a < s.length(); a++)
{
uint8_t ch = s[a];
if(ch == '\'')
{
if(inString && s[a + 1] != '\'')
inString = false;
else
inString = true;
}
if(ch == '`' && !inString)
ch = '"';
query += ch;
}
return query;
}
const char* PgSQLResult::getDataStream(const std::string& s, uint64_t& size)
{
std::string buf = PQgetvalue(m_handle, m_cursor, PQfnumber(m_handle, s.c_str()));
uint8_t* temp = PQunescapeBytea( (const uint8_t*)buf.c_str(), (size_t*)&size);
char* value = new char[buf.size()];
strcpy(value, (char*)temp);
PQfreemem(temp);
return value;
}
void PgSQLResult::free()
{
if(m_handle)
{
PQclear(m_handle);
delete this;
}
else
std::cout << "[Warning - PgSQLResult::free] Trying to free already freed result." << std::endl;
}
bool PgSQLResult::next()
{
if(m_cursor >= m_rows)
return false;
m_cursor++;
return true;
}
PgSQLResult::PgSQLResult(PGresult* results)
{
if(!res)
{
delete this;
return;
}
m_handle = results;
m_cursor = -1;
m_rows = PQntuples(m_handle) - 1;
}