//////////////////////////////////////////////////////////////////////// // 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 #include "database.h" #include "databasesqlite.h" #include "tools.h" #include "configmanager.h" extern ConfigManager g_config; #if SQLITE_VERSION_NUMBER < 3003009 #define OTSYS_SQLITE3_PREPARE sqlite3_prepare #else #define OTSYS_SQLITE3_PREPARE sqlite3_prepare_v2 #endif DatabaseSQLite::DatabaseSQLite() { OTSYS_THREAD_LOCKVARINIT(sqliteLock); m_connected = false; // test for existence of database file; // sqlite3_open will create a new one if it isn't there (what we don't want) if(!fileExists(g_config.getString(ConfigManager::SQL_FILE).c_str())) return; // Initialize sqlite if(sqlite3_open(g_config.getString(ConfigManager::SQL_FILE).c_str(), &m_handle) != SQLITE_OK) { std::cout << "Failed to initialize SQLite connection." << std::endl; sqlite3_close(m_handle); } else m_connected = true; } bool DatabaseSQLite::getParam(DBParam_t param) { switch(param) { case DBPARAM_MULTIINSERT: default: break; } return false; } std::string DatabaseSQLite::_parse(const std::string &s) { std::string query = ""; query.reserve(s.size()); bool inString = false; for(uint32_t i = 0; i < s.length(); i++) { uint8_t ch = s[i]; if(ch == '\'') { if(inString && s[i + 1] != '\'') inString = false; else inString = true; } if(ch == '`' && !inString) ch = '"'; query += ch; } return query; } bool DatabaseSQLite::executeQuery(const std::string &query) { OTSYS_THREAD_LOCK_CLASS lockClass(sqliteLock); if(!m_connected) return false; #ifdef __SQL_QUERY_DEBUG__ std::cout << "SQLITE QUERY: " << query << std::endl; #endif std::string buf = _parse(query); sqlite3_stmt* stmt; // prepares statement if(OTSYS_SQLITE3_PREPARE(m_handle, buf.c_str(), buf.length(), &stmt, NULL) != SQLITE_OK) { sqlite3_finalize(stmt); std::cout << "OTSYS_SQLITE3_PREPARE(): SQLITE ERROR: " << sqlite3_errmsg(m_handle) << " (" << buf << ")" << std::endl; return false; } // executes it once int32_t ret = sqlite3_step(stmt); if(ret != SQLITE_OK && ret != SQLITE_DONE && ret != SQLITE_ROW) { sqlite3_finalize(stmt); std::cout << "sqlite3_step(): SQLITE ERROR: " << sqlite3_errmsg(m_handle) << std::endl; return false; } // closes statement // at all not sure if it should be debugged - query was executed correctly... sqlite3_finalize(stmt); return true; } DBResult* DatabaseSQLite::storeQuery(const std::string &query) { OTSYS_THREAD_LOCK_CLASS lockClass(sqliteLock); if(!m_connected) return NULL; #ifdef __SQL_QUERY_DEBUG__ std::cout << "SQLITE QUERY: " << query << std::endl; #endif std::string buf = _parse(query); sqlite3_stmt* stmt; // prepares statement if(OTSYS_SQLITE3_PREPARE(m_handle, buf.c_str(), buf.length(), &stmt, NULL) != SQLITE_OK) { sqlite3_finalize(stmt); std::cout << "OTSYS_SQLITE3_PREPARE(): SQLITE ERROR: " << sqlite3_errmsg(m_handle) << " (" << buf << ")" << std::endl; return NULL; } DBResult* result = new SQLiteResult(stmt); return verifyResult(result); } std::string DatabaseSQLite::escapeString(const std::string &s) { // remember about quoiting even an empty string! if(!s.size()) return std::string("''"); // the worst case is 2n + 3 char* output = new char[s.length() * 2 + 3]; // quotes escaped string and frees temporary buffer sqlite3_snprintf(s.length() * 2 + 1, output, "%Q", s.c_str()); std::string r(output); delete[] output; //escape % and _ because we are using LIKE operator. r = boost::regex_replace(r, boost::regex("%"), "\\%"); r = boost::regex_replace(r, boost::regex("_"), "\\_"); if(r[r.length() - 1] != '\'') r += "'"; return r; } std::string DatabaseSQLite::escapeBlob(const char* s, uint32_t length) { std::string buf = "x'"; char* hex = new char[2 + 1]; //need one extra byte for null-character for(uint32_t i = 0; i < length; i++) { sprintf(hex, "%02x", ((uint8_t)s[i])); buf += hex; } delete[] hex; buf += "'"; return buf; } int32_t SQLiteResult::getDataInt(const std::string &s) { listNames_t::iterator it = m_listNames.find(s); if(it != m_listNames.end()) return sqlite3_column_int(m_handle, it->second); std::cout << "Error during getDataInt(" << s << ")." << std::endl; return 0; // Failed } int64_t SQLiteResult::getDataLong(const std::string &s) { listNames_t::iterator it = m_listNames.find(s); if(it != m_listNames.end()) return sqlite3_column_int64(m_handle, it->second); std::cout << "Error during getDataLong(" << s << ")." << std::endl; return 0; // Failed } std::string SQLiteResult::getDataString(const std::string &s) { listNames_t::iterator it = m_listNames.find(s); if(it != m_listNames.end() ) { std::string value = (const char*)sqlite3_column_text(m_handle, it->second); return value; } std::cout << "Error during getDataString(" << s << ")." << std::endl; return std::string(""); // Failed } const char* SQLiteResult::getDataStream(const std::string &s, uint64_t &size) { listNames_t::iterator it = m_listNames.find(s); if(it != m_listNames.end()) { const char* value = (const char*)sqlite3_column_blob(m_handle, it->second); size = sqlite3_column_bytes(m_handle, it->second); return value; } std::cout << "Error during getDataStream(" << s << ")." << std::endl; return NULL; // Failed } void SQLiteResult::free() { if(m_handle) { sqlite3_finalize(m_handle); delete this; } else std::cout << "[Warning - SQLiteResult::free] Trying to free already freed result." << std::endl; } SQLiteResult::SQLiteResult(sqlite3_stmt* stmt) { if(!stmt) { delete this; return; } m_handle = stmt; m_listNames.clear(); int32_t fields = sqlite3_column_count(m_handle); for(int32_t i = 0; i < fields; i++) m_listNames[sqlite3_column_name(m_handle, i)] = i; }