#include "stdafx.h" void CDbxSQLite::InitSettings() { sqlite3_stmt *stmt = nullptr; sqlite3_prepare_v2(m_db, "SELECT type, value, contact_id, module, setting FROM settings;", -1, &stmt, nullptr); while (sqlite3_step(stmt) == SQLITE_ROW) { MCONTACT hContact = sqlite3_column_int64(stmt, 2); auto *szModule = (const char *)sqlite3_column_text(stmt, 3); auto *szSetting = (const char *)sqlite3_column_text(stmt, 4); size_t settingNameLen = strlen(szSetting); size_t moduleNameLen = strlen(szModule); char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen); DBVARIANT *dbv = m_cache->GetCachedValuePtr(hContact, szCachedSettingName, 1); if (dbv == nullptr) // garbage! a setting for removed/non-existent contact continue; dbv->type = (int)sqlite3_column_int(stmt, 0); switch (dbv->type) { case DBVT_BYTE: dbv->bVal = sqlite3_column_int(stmt, 1); break; case DBVT_WORD: dbv->wVal = sqlite3_column_int(stmt, 1); break; case DBVT_DWORD: dbv->dVal = sqlite3_column_int64(stmt, 1); break; case DBVT_ASCIIZ: case DBVT_UTF8: dbv->cchVal = sqlite3_column_bytes(stmt, 1); { const char *value = (const char *)sqlite3_column_text(stmt, 1); dbv->pszVal = (char *)mir_alloc(dbv->cchVal + 1); memcpy(dbv->pszVal, value, dbv->cchVal); dbv->pszVal[dbv->cchVal] = 0; } break; case DBVT_ENCRYPTED: case DBVT_BLOB: dbv->cpbVal = sqlite3_column_bytes(stmt, 1); { const char *data = (const char *)sqlite3_column_blob(stmt, 1); dbv->pbVal = (uint8_t *)mir_alloc(dbv->cpbVal + 1); memcpy(dbv->pbVal, data, dbv->cpbVal); dbv->pbVal[dbv->cpbVal] = 0; } break; } } sqlite3_finalize(stmt); FillContactSettings(); DBVARIANT dbv = { DBVT_BYTE }; if (GetContactSettingWorker(0, "Compatibility", "Sqlite", &dbv, 0)) dbv.bVal = 0; if (dbv.bVal < 1) { int rc = sqlite3_exec(m_db, "ALTER TABLE events ADD COLUMN is_read INTEGER NOT NULL DEFAULT 0;", 0, 0, 0); logError(rc, __FILE__, __LINE__); rc = sqlite3_exec(m_db, "CREATE INDEX idx_events_isread ON events(contact_id, is_read, timestamp);", nullptr, nullptr, nullptr); logError(rc, __FILE__, __LINE__); rc = sqlite3_exec(m_db, "UPDATE events SET is_read=1 WHERE (flags & 6) <> 0;", nullptr, nullptr, nullptr); logError(rc, __FILE__, __LINE__); dbv.bVal = 1; } if (dbv.bVal < 2) { int rc = sqlite3_exec(m_db, "ALTER TABLE events ADD COLUMN user_id TEXT NULL;", 0, 0, 0); logError(rc, __FILE__, __LINE__); dbv.bVal = 2; } if (dbv.bVal < 3) { int rc = sqlite3_exec(m_db, "UPDATE events_srt SET timestamp=timestamp*1000;", 0, 0, 0); logError(rc, __FILE__, __LINE__); CQuery updateSql; sqlite3_stmt *updateStmt = InitQuery("update events_srt set timestamp=timestamp+? where id in (select max(id) from events_srt where contact_id=? and timestamp=?);", updateSql); sqlite3_stmt *pQuery; rc = sqlite3_prepare_v2(m_db, "SELECT contact_id, timestamp, count(*) AS boo FROM events_srt GROUP BY contact_id, timestamp HAVING boo > 1;", -1, &pQuery, nullptr); logError(rc, __FILE__, __LINE__); while (sqlite3_step(pQuery) == SQLITE_ROW) { MCONTACT hContact = sqlite3_column_int(pQuery, 0); int64_t ts = sqlite3_column_int64(pQuery, 1); int iCount = sqlite3_column_int(pQuery, 2); for (int i = iCount - 1; i > 0; i--) { sqlite3_bind_int64(updateStmt, 1, i); sqlite3_bind_int64(updateStmt, 2, hContact); sqlite3_bind_int64(updateStmt, 3, ts); rc = sqlite3_step(updateStmt); logError(rc, __FILE__, __LINE__); sqlite3_reset(updateStmt); } } rc = sqlite3_exec(m_db, "CREATE TABLE tmp(id INTEGER NOT NULL, contact_id INTEGER NOT NULL, timestamp INTEGER, PRIMARY KEY(contact_id, timestamp));", 0, 0, 0); logError(rc, __FILE__, __LINE__); rc = sqlite3_exec(m_db, "INSERT INTO tmp SELECT * FROM events_srt;", 0, 0, 0); logError(rc, __FILE__, __LINE__); rc = sqlite3_exec(m_db, "DROP TABLE events_srt;", 0, 0, 0); logError(rc, __FILE__, __LINE__); rc = sqlite3_exec(m_db, "ALTER TABLE tmp RENAME TO events_srt;", 0, 0, 0); logError(rc, __FILE__, __LINE__); rc = sqlite3_exec(m_db, "VACUUM;", 0, 0, 0); logError(rc, __FILE__, __LINE__); dbv.bVal = 3; } if (dbv.bVal < 5) { int rc = sqlite3_exec(m_db, "DROP INDEX IF EXISTS i1_srt;", 0, 0, 0); logError(rc, __FILE__, __LINE__); rc = sqlite3_exec(m_db, "CREATE INDEX i1_srt ON events_srt(id);", 0, 0, 0); logError(rc, __FILE__, __LINE__); dbv.bVal = 5; WriteContactSetting(0, "Compatibility", "Sqlite", &dbv); } } ///////////////////////////////////////////////////////////////////////////////////////// BOOL CDbxSQLite::EnumModuleNames(DBMODULEENUMPROC pFunc, void *param) { LIST modules(100); { sqlite3_stmt *stmt = InitQuery("SELECT DISTINCT module FROM settings;", qSettModules); int rc = 0; while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { const char *value = (const char *)sqlite3_column_text(stmt, 0); modules.insert(mir_strdup(value)); } logError(rc, __FILE__, __LINE__); sqlite3_reset(stmt); } int result = -1; for (auto &module : modules) { result = pFunc(module, param); mir_free(module); } return result; } ///////////////////////////////////////////////////////////////////////////////////////// BOOL CDbxSQLite::WriteContactSettingWorker(MCONTACT hContact, DBCONTACTWRITESETTING &dbcws) { sqlite3_stmt *stmt = InitQuery("REPLACE INTO settings(contact_id, module, setting, type, value) VALUES (?, ?, ?, ?, ?);", qSettWrite); sqlite3_bind_int64(stmt, 1, hContact); sqlite3_bind_text(stmt, 2, dbcws.szModule, (int)mir_strlen(dbcws.szModule), nullptr); sqlite3_bind_text(stmt, 3, dbcws.szSetting, (int)mir_strlen(dbcws.szSetting), nullptr); sqlite3_bind_int(stmt, 4, dbcws.value.type); switch (dbcws.value.type) { case DBVT_BYTE: sqlite3_bind_int(stmt, 5, dbcws.value.bVal); break; case DBVT_WORD: sqlite3_bind_int(stmt, 5, dbcws.value.wVal); break; case DBVT_DWORD: sqlite3_bind_int64(stmt, 5, dbcws.value.dVal); break; case DBVT_ASCIIZ: case DBVT_UTF8: sqlite3_bind_text(stmt, 5, dbcws.value.pszVal, dbcws.value.cchVal, nullptr); break; case DBVT_ENCRYPTED: case DBVT_BLOB: sqlite3_bind_blob(stmt, 5, dbcws.value.pbVal, dbcws.value.cpbVal, nullptr); break; } int rc = sqlite3_step(stmt); logError(rc, __FILE__, __LINE__); sqlite3_reset(stmt); if (rc != SQLITE_DONE) return 1; DBFlush(); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// int CDbxSQLite::DeleteContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting) { mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = InitQuery("DELETE FROM settings WHERE contact_id = ? AND module = ? AND setting = ?;", qSettDel); sqlite3_bind_int64(stmt, 1, hContact); sqlite3_bind_text(stmt, 2, szModule, (int)mir_strlen(szModule), nullptr); sqlite3_bind_text(stmt, 3, szSetting, (int)mir_strlen(szSetting), nullptr); int rc = sqlite3_step(stmt); logError(rc, __FILE__, __LINE__); sqlite3_reset(stmt); return rc; } BOOL CDbxSQLite::DeleteContactSetting(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting) { if (szSetting == nullptr || szModule == nullptr) return 1; if (hContact) { DBCachedContact *cc = m_cache->GetCachedContact(hContact); if (cc == nullptr) return 1; } // if a setting isn't found in cache, then return an error - we don't cache misses anymore char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, mir_strlen(szModule), mir_strlen(szSetting)); if (m_cache->GetCachedValuePtr(hContact, szCachedSettingName, -1) == nullptr) return 1; if (szCachedSettingName[-1] == 0) { // it's not a resident variable DeleteContactSettingWorker(hContact, szModule, szSetting); DBFlush(); } // notify DBCONTACTWRITESETTING dbcws = { 0 }; dbcws.szModule = szModule; dbcws.szSetting = szSetting; dbcws.value.type = DBVT_DELETED; NotifyEventHooks(g_hevSettingChanged, hContact, (LPARAM)&dbcws); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// BOOL CDbxSQLite::EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param) { if (szModule == nullptr) return -1; if (hContact) { DBCachedContact *cc = m_cache->GetCachedContact(hContact); if (cc == nullptr) return -1; } LIST settings(100); { mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = InitQuery("SELECT setting FROM settings WHERE contact_id = ? AND module = ?;", qSettEnum); sqlite3_bind_int64(stmt, 1, hContact); sqlite3_bind_text(stmt, 2, szModule, (int)mir_strlen(szModule), nullptr); int rc = 0; while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { const char *value = (const char *)sqlite3_column_text(stmt, 0); settings.insert(mir_strdup(value)); } logError(rc, __FILE__, __LINE__); sqlite3_reset(stmt); } int result = -1; for (auto &setting : settings) { result = pfnEnumProc(setting, param); mir_free(setting); } return result; }