diff options
Diffstat (limited to 'plugins/Dbx_sqlite/src/dbsettings.cpp')
-rw-r--r-- | plugins/Dbx_sqlite/src/dbsettings.cpp | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/plugins/Dbx_sqlite/src/dbsettings.cpp b/plugins/Dbx_sqlite/src/dbsettings.cpp new file mode 100644 index 0000000000..121f2c73fc --- /dev/null +++ b/plugins/Dbx_sqlite/src/dbsettings.cpp @@ -0,0 +1,368 @@ +#include "stdafx.h" + +enum { + SQL_SET_STMT_ENUM = 0, + SQL_SET_STMT_GET, + SQL_SET_STMT_REPLACE, + SQL_SET_STMT_DELETE, + SQL_SET_STMT_ENUMMODULE, + SQL_SET_STMT_NUM +}; + +static char *settings_stmts[SQL_SET_STMT_NUM] = { + "select distinct module from settings;", + "select type, value from settings where contactid = ? and module = ? and setting = ? limit 1;", + "replace into settings(contactid, module, setting, type, value) values (?, ?, ?, ?, ?);", + "delete from settings where contactid = ? and module = ? and setting = ?;", + "select setting from settings where contactid = ? and module = ?;" +}; + +static sqlite3_stmt *settings_stmts_prep[SQL_SET_STMT_NUM] = { 0 }; + +void CDbxSQLite::InitSettings() +{ + for (size_t i = 0; i < SQL_SET_STMT_NUM; i++) + sqlite3_prepare_v3(m_db, settings_stmts[i], -1, SQLITE_PREPARE_PERSISTENT, &settings_stmts_prep[i], nullptr); + /*DBVARIANT dbv = {}; + sqlite3_stmt *stmt = nullptr; + sqlite3_prepare_v2(m_db, "select contactid, module, setting, type, value from settings", -1, &stmt, nullptr); + while (sqlite3_step(stmt) == SQLITE_ROW) { + MCONTACT hContact = sqlite3_column_int64(stmt, 0); + const char *module = (const char*)sqlite3_column_text(stmt, 1); + const char *setting = (const char*)sqlite3_column_text(stmt, 2); + dbv.type = sqlite3_column_int(stmt, 3); + switch (dbv.type) { + case DBVT_BYTE: + dbv.bVal = sqlite3_column_int(stmt, 4); + break; + case DBVT_WORD: + dbv.wVal = sqlite3_column_int(stmt, 4); + break; + case DBVT_DWORD: + dbv.dVal = sqlite3_column_int64(stmt, 4); + break; + case DBVT_ASCIIZ: + { + char *utf8 = mir_strdup((const char*)sqlite3_column_text(stmt, 4)); + dbv.pszVal = mir_utf8decode(utf8, nullptr); + break; + } + case DBVT_UTF8: + dbv.pszVal = mir_strdup((const char*)sqlite3_column_text(stmt, 4)); + break; + case DBVT_BLOB: + continue; + } + char *cachedSettingName = m_cache->GetCachedSetting(module, setting, mir_strlen(module), mir_strlen(setting)); + DBVARIANT *cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1); + m_cache->SetCachedVariant(&dbv, cachedValue); + if(dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_UTF8) + mir_free(dbv.pszVal); + } + sqlite3_finalize(stmt);*/ +} + +void CDbxSQLite::UninitSettings() +{ + for (size_t i = 0; i < SQL_SET_STMT_NUM; i++) + sqlite3_finalize(settings_stmts_prep[i]); +} + +BOOL CDbxSQLite::EnumModuleNames(DBMODULEENUMPROC pFunc, void *param) +{ + LIST<char> modules(100); + { + sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_ENUM]; + while (sqlite3_step(stmt) == SQLITE_ROW) { + const char *value = (const char*)sqlite3_column_text(stmt, 0); + modules.insert(mir_strdup(value)); + } + sqlite3_reset(stmt); + } + + int result = -1; + for (auto &module : modules) { + result = pFunc(module, param); + mir_free(module); + } + return result; +} + +BOOL CDbxSQLite::GetContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic) +{ + if (szSetting == nullptr || szModule == nullptr) + return 1; + + if (hContact) { + DBCachedContact *cc = m_cache->GetCachedContact(hContact); + if (cc == nullptr) + return 1; + } + + mir_cslock lock(m_csDbAccess); + + char *cachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, mir_strlen(szModule), mir_strlen(szSetting)); + DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 0); + if (pCachedValue != nullptr) { + if (pCachedValue->type == DBVT_UTF8) { + int cbOrigLen = dbv->cchVal; + char *cbOrigPtr = dbv->pszVal; + memcpy(dbv, pCachedValue, sizeof(DBVARIANT)); + if (isStatic) { + int cbLen = 0; + if (pCachedValue->pszVal != nullptr) + cbLen = (int)mir_strlen(pCachedValue->pszVal); + + cbOrigLen--; + dbv->pszVal = cbOrigPtr; + if (cbLen < cbOrigLen) + cbOrigLen = cbLen; + memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen); + dbv->pszVal[cbOrigLen] = 0; + dbv->cchVal = cbLen; + } + else { + dbv->pszVal = (char*)mir_alloc(mir_strlen(pCachedValue->pszVal) + 1); + mir_strcpy(dbv->pszVal, pCachedValue->pszVal); + } + } + else memcpy(dbv, pCachedValue, sizeof(DBVARIANT)); + + return (pCachedValue->type == DBVT_DELETED) ? 1 : 0; + } + + // never look db for the resident variable + if (cachedSettingName[-1] != 0) + return 1; + + sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_GET]; + sqlite3_bind_int64(stmt, 1, hContact); + sqlite3_bind_text(stmt, 2, szModule, mir_strlen(szModule), nullptr); + sqlite3_bind_text(stmt, 3, szSetting, mir_strlen(szSetting), nullptr); + int rc = sqlite3_step(stmt); + if (rc != SQLITE_ROW) { + sqlite3_reset(stmt); + return 1; + } + dbv->type = (int)sqlite3_column_int(stmt, 0); + switch (dbv->type) { + case DBVT_DELETED: + dbv->type = DBVT_DELETED; + sqlite3_reset(stmt); + return 2; + 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_UTF8: + { + dbv->cchVal = sqlite3_column_bytes(stmt, 1); + const char *value = (const char*)sqlite3_column_text(stmt, 1); + if (!isStatic) + dbv->pszVal = (char*)mir_alloc(dbv->cchVal + 1); + memcpy(dbv->pszVal, value, dbv->cchVal); + dbv->pszVal[dbv->cchVal] = 0; + break; + } + case DBVT_BLOB: + { + dbv->cpbVal = sqlite3_column_bytes(stmt, 1); + const char *data = (const char*)sqlite3_column_blob(stmt, 1); + if (!isStatic) + dbv->pbVal = (BYTE*)mir_alloc(dbv->cpbVal + 1); + memcpy(dbv->pbVal, data, dbv->cpbVal); + break; + } + } + sqlite3_reset(stmt); + + // add to cache + if (dbv->type != DBVT_BLOB) { + pCachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1); + if (pCachedValue != nullptr) + m_cache->SetCachedVariant(dbv, pCachedValue); + } + + return 0; +} + +BOOL CDbxSQLite::WriteContactSetting(MCONTACT hContact, DBCONTACTWRITESETTING *dbcws) +{ + if (dbcws == nullptr || dbcws->szSetting == nullptr || dbcws->szModule == nullptr) + return 1; + + if (hContact) { + DBCachedContact *cc = m_cache->GetCachedContact(hContact); + if (cc == nullptr) + return 1; + } + + DBCONTACTWRITESETTING dbcwNotif = *dbcws; + // we work only with utf-8 inside + switch (dbcwNotif.value.type) { + case DBVT_UTF8: + dbcwNotif.value.pszVal = mir_strdup(dbcws->value.pszVal); + break; + case DBVT_ASCIIZ: + { + ptrA value(mir_utf8encode(dbcws->value.pszVal)); + dbcwNotif.value.pszVal = NEWSTR_ALLOCA(value); + dbcwNotif.value.type = DBVT_UTF8; + break; + } + case DBVT_WCHAR: + { + T2Utf value(dbcwNotif.value.pwszVal); + dbcwNotif.value.pszVal = NEWSTR_ALLOCA(value); + dbcwNotif.value.type = DBVT_UTF8; + break; + } + } + DBCONTACTWRITESETTING dbcwWork = dbcwNotif; + if (dbcwWork.value.type == DBVT_UTF8) + dbcwWork.value.cchVal = (WORD)strlen(dbcwWork.value.pszVal); + + mir_cslockfull lock(m_csDbAccess); + + char *cachedSettingName = m_cache->GetCachedSetting(dbcwWork.szModule, dbcwWork.szSetting, mir_strlen(dbcwWork.szModule), mir_strlen(dbcwWork.szSetting)); + bool isResident = cachedSettingName[-1] != 0; + + // we don't cache blobs + if (dbcwWork.value.type != DBVT_BLOB) { + DBVARIANT *cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1); + if (cachedValue != nullptr) { + bool isIdentical = false; + if (cachedValue->type == dbcwWork.value.type) { + switch (dbcwWork.value.type) { + case DBVT_BYTE: + isIdentical = cachedValue->bVal == dbcwWork.value.bVal; + break; + case DBVT_WORD: + isIdentical = cachedValue->wVal == dbcwWork.value.wVal; + break; + case DBVT_DWORD: + isIdentical = cachedValue->dVal == dbcwWork.value.dVal; + break; + case DBVT_UTF8: + isIdentical = mir_strcmp(cachedValue->pszVal, dbcwWork.value.pszVal) == 0; + break; + } + if (isIdentical) + return 0; + } + m_cache->SetCachedVariant(&dbcwWork.value, cachedValue); + } + if (isResident) { + lock.unlock(); + NotifyEventHooks(hSettingChangeEvent, hContact, (LPARAM)&dbcwWork); + return 0; + } + } + else m_cache->GetCachedValuePtr(hContact, cachedSettingName, -1); + + sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_REPLACE]; + sqlite3_bind_int64(stmt, 1, hContact); + sqlite3_bind_text(stmt, 2, dbcwWork.szModule, mir_strlen(dbcwWork.szModule), nullptr); + sqlite3_bind_text(stmt, 3, dbcwWork.szSetting, mir_strlen(dbcwWork.szSetting), nullptr); + sqlite3_bind_int(stmt, 4, dbcwWork.value.type); + switch (dbcwWork.value.type) { + case DBVT_BYTE: + sqlite3_bind_int(stmt, 5, dbcwWork.value.bVal); + break; + case DBVT_WORD: + sqlite3_bind_int(stmt, 5, dbcwWork.value.wVal); + break; + case DBVT_DWORD: + sqlite3_bind_int64(stmt, 5, dbcwWork.value.dVal); + break; + case DBVT_UTF8: + sqlite3_bind_text(stmt, 5, dbcwWork.value.pszVal, dbcwWork.value.cchVal, nullptr); + break; + case DBVT_BLOB: + sqlite3_bind_blob(stmt, 5, dbcwWork.value.pbVal, dbcwWork.value.cpbVal, nullptr); + break; + } + int rc = sqlite3_step(stmt); + sqlite3_reset(stmt); + if (rc != SQLITE_DONE) + return 1; + + lock.unlock(); + + NotifyEventHooks(hSettingChangeEvent, hContact, (LPARAM)&dbcwNotif); + + return 0; +} + +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; + } + + char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, mir_strlen(szModule), mir_strlen(szSetting)); + if (szCachedSettingName[-1] == 0) // it's not a resident variable + { + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_DELETE]; + sqlite3_bind_int64(stmt, 1, hContact); + sqlite3_bind_text(stmt, 2, szModule, mir_strlen(szModule), nullptr); + sqlite3_bind_text(stmt, 3, szSetting, mir_strlen(szSetting), nullptr); + int rc = sqlite3_step(stmt); + sqlite3_reset(stmt); + if (rc != SQLITE_DONE) + return 1; + } + m_cache->GetCachedValuePtr(hContact, szCachedSettingName, -1); + + // notify + DBCONTACTWRITESETTING dbcws = { }; + dbcws.szModule = szModule; + dbcws.szSetting = szSetting; + dbcws.value.type = DBVT_DELETED; + NotifyEventHooks(hSettingChangeEvent, 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<char> settings(100); + { + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_ENUMMODULE]; + sqlite3_bind_int64(stmt, 1, hContact); + sqlite3_bind_text(stmt, 2, szModule, mir_strlen(szModule), nullptr); + while (sqlite3_step(stmt) == SQLITE_ROW) { + const char *value = (const char*)sqlite3_column_text(stmt, 0); + settings.insert(mir_strdup(value)); + } + sqlite3_reset(stmt); + } + + int result = -1; + for (auto &setting : settings) { + result = pfnEnumProc(setting, param); + mir_free(setting); + } + return result; +} |