#include "stdafx.h" enum { SQL_EVT_STMT_COUNT = 0, SQL_EVT_STMT_ADDEVENT, SQL_EVT_STMT_ADDCONTACTEVENT, SQL_EVT_STMT_DELETE, SQL_EVT_STMT_EDIT, SQL_EVT_STMT_BLOBSIZE, SQL_EVT_STMT_GET, SQL_EVT_STMT_GETFLAGS, SQL_EVT_STMT_SETFLAGS, SQL_EVT_STMT_GETCONTACT, SQL_EVT_STMT_FINDFIRST, SQL_EVT_STMT_FINDFIRSTUNREAD, SQL_EVT_STMT_FINDLAST, SQL_EVT_STMT_FINDNEXT, SQL_EVT_STMT_FINDPREV, SQL_EVT_STMT_GETIDBYSRVID, SQL_EVT_STMT_SETSRVID, SQL_EVT_STMT_MERGE, SQL_EVT_STMT_SPLIT, SQL_EVT_STMT_NUM }; static char *evt_stmts[SQL_EVT_STMT_NUM] = { "select count(1) from contact_events where contact_id = ? limit 1;", "insert into events(module, timestamp, type, flags, size, data) values (?, ?, ?, ?, ?, ?);", "insert into contact_events(contact_id, event_id, timestamp) values (?, ?, ?);", "delete from contact_events where contact_id = ?1 and event_id = ?2; delete from events where id = ?2;", "update events set module = ?, timestamp = ?, type = ?, flags = ?, size = ?, blob = ? where id = ?;", "select size from events where id = ? limit 1;", "select module, timestamp, type, flags, size, data from events where id = ? limit 1;", "select flags from events where id = ? limit 1;", "update events set flags = ? where id = ?;", "select contact_id from contact_events where event_id = ? limit 1;", "select event_id from contact_events where contact_id = ? order by timestamp, event_id limit 1;", "select events.id from events join contact_events on contact_events.event_id = events.id where contact_events.contact_id = ? and (events.flags & ?) = 0 order by events.timestamp, events.id limit 1;", "select event_id from contact_events where contact_id = ? order by timestamp desc, event_id desc limit 1;", "select event_id from contact_events where contact_id = ?1 and event_id <> ?2 and timestamp > (select timestamp from contact_events where contact_id = ?1 and event_id = ?2 limit 1) order by timestamp, event_id limit 1;", "select event_id from contact_events where contact_id = ? and event_id <> ? and timestamp < (select timestamp from contact_events where contact_id = ?1 and event_id = ?2 limit 1) order by timestamp desc, event_id desc limit 1;", "select id from events where module = ? and server_id = ? limit 1;", "update events set server_id = ? where id = ?;", "insert into contact_events(contact_id, event_id, timestamp) select ?1, event_id, timestamp from contact_events where contact_id = ?2 order by timestamp, event_id;", "delete from contact_events where contact_id = ? and event_id in (select event_id from contact_events where contact_id = ?);", }; static sqlite3_stmt *evt_stmts_prep[SQL_EVT_STMT_NUM] = { 0 }; void CDbxSQLite::InitEvents() { for (size_t i = 0; i < SQL_EVT_STMT_NUM; i++) sqlite3_prepare_v3(m_db, evt_stmts[i], -1, SQLITE_PREPARE_PERSISTENT, &evt_stmts_prep[i], nullptr); } void CDbxSQLite::UninitEvents() { for (size_t i = 0; i < SQL_EVT_STMT_NUM; i++) sqlite3_finalize(evt_stmts_prep[i]); } LONG CDbxSQLite::GetEventCount(MCONTACT hContact) { DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; if (cc->count) return cc->count; mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_COUNT]; sqlite3_bind_int64(stmt, 1, hContact); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc != SQLITE_ROW) { sqlite3_reset(stmt); return 0; } cc->count = sqlite3_column_int64(stmt, 0); sqlite3_reset(stmt); return cc->count; } MEVENT CDbxSQLite::AddEvent(MCONTACT hContact, DBEVENTINFO *dbei) { if (dbei == nullptr) return 0; if (dbei->timestamp == 0) return 0; MCONTACT hNotifyContact = hContact; DBCachedContact *cc, *ccSub = nullptr; if (hContact != 0) { if ((cc = m_cache->GetCachedContact(hContact)) == nullptr) return 0; if (cc->IsSub()) { ccSub = cc; if ((cc = m_cache->GetCachedContact(cc->parentID)) == nullptr) return 0; // set default sub to the event's source if (!(dbei->flags & DBEF_SENT)) db_mc_setDefault(cc->contactID, hContact, false); hContact = cc->contactID; // and add an event to a metahistory if (db_mc_isEnabled()) hNotifyContact = hContact; } } else cc = &m_system; if (cc == nullptr) return 0; if (m_safetyMode) if (NotifyEventHooks(hEventFilterAddedEvent, hNotifyContact, (LPARAM)dbei)) return 0; MEVENT hDbEvent = 0; { sqlite3_exec(m_db, "begin transaction;", nullptr, nullptr, nullptr); mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_ADDEVENT]; sqlite3_bind_text(stmt, 1, dbei->szModule, mir_strlen(dbei->szModule), nullptr); sqlite3_bind_int64(stmt, 2, dbei->timestamp); sqlite3_bind_int(stmt, 3, dbei->eventType); sqlite3_bind_int64(stmt, 4, dbei->flags); sqlite3_bind_int64(stmt, 5, dbei->cbBlob); sqlite3_bind_blob(stmt, 6, dbei->pBlob, dbei->cbBlob, nullptr); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); sqlite3_reset(stmt); if (rc != SQLITE_DONE) { sqlite3_exec(m_db, "rollback;", nullptr, nullptr, nullptr); return 0; } hDbEvent = sqlite3_last_insert_rowid(m_db); stmt = evt_stmts_prep[SQL_EVT_STMT_ADDCONTACTEVENT]; sqlite3_bind_int64(stmt, 1, hContact); sqlite3_bind_int64(stmt, 2, hDbEvent); sqlite3_bind_int64(stmt, 3, dbei->timestamp); rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); sqlite3_reset(stmt); if (rc != SQLITE_DONE) { sqlite3_exec(m_db, "rollback;", nullptr, nullptr, nullptr); return 0; } // insert an event into a sub's history too if (ccSub != nullptr) { sqlite3_bind_int64(stmt, 1, ccSub->contactID); sqlite3_bind_int64(stmt, 2, hDbEvent); sqlite3_bind_int64(stmt, 3, dbei->timestamp); rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); sqlite3_reset(stmt); if (rc != SQLITE_DONE) { sqlite3_exec(m_db, "rollback;", nullptr, nullptr, nullptr); return 0; } } sqlite3_exec(m_db, "commit;", nullptr, nullptr, nullptr); cc->count++; cc->first = cc->last = 0; if (!dbei->markedRead()) cc->unread = 0; } if (m_safetyMode) NotifyEventHooks(hEventAddedEvent, hNotifyContact, (LPARAM)hDbEvent); return hDbEvent; } BOOL CDbxSQLite::DeleteEvent(MCONTACT hContact, MEVENT hDbEvent) { if (hDbEvent == 0) return 1; DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; if (cc == nullptr) return 1; { mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_DELETE]; sqlite3_bind_int64(stmt, 1, hContact); sqlite3_bind_int64(stmt, 2, hDbEvent); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); sqlite3_reset(stmt); if (rc != SQLITE_DONE) return 1; if (cc->count > 0) cc->count--; cc->first = cc->unread = cc->last = 0; } NotifyEventHooks(hEventDeletedEvent, hContact, (LPARAM)hDbEvent); return 0; } BOOL CDbxSQLite::EditEvent(MCONTACT hContact, MEVENT hDbEvent, DBEVENTINFO *dbei) { if (dbei == nullptr) return 1; if (dbei->timestamp == 0) return 1; DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; if (cc == nullptr) return 1; mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_EDIT]; sqlite3_bind_text(stmt, 1, dbei->szModule, mir_strlen(dbei->szModule), nullptr); sqlite3_bind_int64(stmt, 2, dbei->timestamp); sqlite3_bind_int(stmt, 3, dbei->eventType); sqlite3_bind_int64(stmt, 4, dbei->flags); sqlite3_bind_int64(stmt, 5, dbei->cbBlob); sqlite3_bind_blob(stmt, 6, dbei->pBlob, dbei->cbBlob, nullptr); sqlite3_bind_int64(stmt, 7, hDbEvent); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc == SQLITE_DONE) { cc->first = cc->unread = cc->last = 0; NotifyEventHooks(hEventEditedEvent, hContact, (LPARAM)hDbEvent); } sqlite3_reset(stmt); return (rc != SQLITE_DONE); } LONG CDbxSQLite::GetBlobSize(MEVENT hDbEvent) { if (hDbEvent == 0) return -1; mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_BLOBSIZE]; sqlite3_bind_int(stmt, 1, hDbEvent); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc != SQLITE_ROW) { sqlite3_reset(stmt); return -1; } LONG res = sqlite3_column_int64(stmt, 0); sqlite3_reset(stmt); return res; } BOOL CDbxSQLite::GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbei) { if (hDbEvent == 0) return 1; if (dbei == nullptr) return 1; if (dbei->cbBlob > 0 && dbei->pBlob == nullptr) { dbei->cbBlob = 0; return 1; } mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GET]; sqlite3_bind_int64(stmt, 1, hDbEvent); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc != SQLITE_ROW) { sqlite3_reset(stmt); return 1; } dbei->szModule = (char*)sqlite3_column_text(stmt, 0); dbei->timestamp = sqlite3_column_int64(stmt, 1); dbei->eventType = sqlite3_column_int(stmt, 2); dbei->flags = sqlite3_column_int64(stmt, 3); DWORD cbBlob = sqlite3_column_int64(stmt, 4); int bytesToCopy = (dbei->cbBlob < cbBlob) ? dbei->cbBlob : cbBlob; dbei->cbBlob = cbBlob; if (bytesToCopy && dbei->pBlob) { BYTE *data = (BYTE*)sqlite3_column_blob(stmt, 5); memcpy(dbei->pBlob, data, dbei->cbBlob); } sqlite3_reset(stmt); return 0; } BOOL CDbxSQLite::MarkEventRead(MCONTACT hContact, MEVENT hDbEvent) { if (hDbEvent == 0) return -1; DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; if (cc == nullptr) return -1; DWORD flags = 0; { mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GETFLAGS]; sqlite3_bind_int64(stmt, 1, hDbEvent); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc != SQLITE_ROW) { sqlite3_reset(stmt); return -1; } flags = sqlite3_column_int64(stmt, 0); sqlite3_reset(stmt); } if ((flags & DBEF_READ) == DBEF_READ) return flags; flags |= DBEF_READ; { mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_SETFLAGS]; sqlite3_bind_int(stmt, 1, flags); sqlite3_bind_int64(stmt, 2, hDbEvent); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); sqlite3_reset(stmt); if (rc != SQLITE_DONE) return -1; if (cc->unread = hDbEvent) cc->unread = 0; } NotifyEventHooks(hEventMarkedRead, hContact, (LPARAM)hDbEvent); return flags; } MCONTACT CDbxSQLite::GetEventContact(MEVENT hDbEvent) { if (hDbEvent == 0) return INVALID_CONTACT_ID; mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GETCONTACT]; sqlite3_bind_int64(stmt, 1, hDbEvent); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc != SQLITE_ROW) { sqlite3_reset(stmt); return INVALID_CONTACT_ID; } MCONTACT hContact = sqlite3_column_int64(stmt, 0); sqlite3_reset(stmt); return hContact; } MEVENT CDbxSQLite::FindFirstEvent(MCONTACT hContact) { DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; if (cc == nullptr) return 0; if (cc->first) return cc->first; mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_FINDFIRST]; sqlite3_bind_int64(stmt, 1, hContact); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc != SQLITE_ROW) { sqlite3_reset(stmt); return 0; } cc->first = sqlite3_column_int64(stmt, 0); sqlite3_reset(stmt); return cc->first; } MEVENT CDbxSQLite::FindFirstUnreadEvent(MCONTACT hContact) { DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; if (cc == nullptr) return 0; if (cc->unread) return cc->unread; mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]; sqlite3_bind_int64(stmt, 1, hContact); sqlite3_bind_int(stmt, 2, DBEF_READ | DBEF_SENT); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc != SQLITE_ROW) { sqlite3_reset(stmt); return 0; } cc->unread = sqlite3_column_int64(stmt, 0); sqlite3_reset(stmt); return cc->unread; } MEVENT CDbxSQLite::FindLastEvent(MCONTACT hContact) { DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; if (cc == nullptr) return 0; if (cc->last) return cc->last; mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_FINDLAST]; sqlite3_bind_int64(stmt, 1, hContact); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc != SQLITE_ROW) { sqlite3_reset(stmt); return 0; } cc->last = sqlite3_column_int64(stmt, 0); sqlite3_reset(stmt); return cc->last; } MEVENT CDbxSQLite::FindNextEvent(MCONTACT hContact, MEVENT hDbEvent) { if (hDbEvent == 0) return 0; DBCachedContact *cc = m_cache->GetCachedContact(hContact); if (cc == nullptr) return 0; mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_FINDNEXT]; sqlite3_bind_int64(stmt, 1, hContact); sqlite3_bind_int64(stmt, 2, hDbEvent); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc != SQLITE_ROW) { sqlite3_reset(stmt); return 0; } hDbEvent = sqlite3_column_int64(stmt, 0); sqlite3_reset(stmt); return hDbEvent; } MEVENT CDbxSQLite::FindPrevEvent(MCONTACT hContact, MEVENT hDbEvent) { if (hDbEvent == 0) return 0; DBCachedContact *cc = m_cache->GetCachedContact(hContact); if (cc == nullptr) return 0; mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_FINDPREV]; sqlite3_bind_int64(stmt, 1, hContact); sqlite3_bind_int64(stmt, 2, hDbEvent); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc != SQLITE_ROW) { sqlite3_reset(stmt); return 0; } hDbEvent = sqlite3_column_int64(stmt, 0); sqlite3_reset(stmt); return hDbEvent; } MEVENT CDbxSQLite::GetEventById(LPCSTR szModule, LPCSTR szId) { if (szModule == nullptr || szId == nullptr) return 0; mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GETIDBYSRVID]; sqlite3_bind_text(stmt, 1, szModule, mir_strlen(szModule), nullptr); sqlite3_bind_text(stmt, 2, szId, mir_strlen(szId), nullptr); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); if (rc != SQLITE_ROW) { sqlite3_reset(stmt); return 0; } MEVENT hDbEvent = sqlite3_column_int64(stmt, 0); sqlite3_reset(stmt); return hDbEvent; } BOOL CDbxSQLite::SetEventId(LPCSTR, MEVENT hDbEvent, LPCSTR szId) { if (hDbEvent == 0 || szId == nullptr) return 1; mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_SETSRVID]; sqlite3_bind_text(stmt, 1, szId, mir_strlen(szId), nullptr); sqlite3_bind_int64(stmt, 2, hDbEvent); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); sqlite3_reset(stmt); return (rc != SQLITE_DONE); } BOOL CDbxSQLite::MetaMergeHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) { mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_MERGE]; sqlite3_bind_int64(stmt, 1, ccMeta->contactID); sqlite3_bind_int64(stmt, 2, ccSub->contactID); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); sqlite3_reset(stmt); ccMeta->first = ccMeta->unread = ccMeta->last = 0; return (rc != SQLITE_DONE); } BOOL CDbxSQLite::MetaSplitHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) { mir_cslock lock(m_csDbAccess); sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_MERGE]; sqlite3_bind_int64(stmt, 1, ccMeta->contactID); sqlite3_bind_int64(stmt, 2, ccSub->contactID); int rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW || rc == SQLITE_DONE); sqlite3_reset(stmt); ccMeta->first = ccMeta->unread = ccMeta->last = 0; return (rc != SQLITE_DONE); }