Commit 07443b1f authored by Ronan's avatar Ronan

feat(MainDb): increase performance of get history range

parent 9fa36be4
......@@ -272,15 +272,12 @@ set(LINPHONE_OBJC_SOURCE_FILES)
if (APPLE)
list(APPEND LINPHONE_OBJC_SOURCE_FILES core/paths/paths-apple.mm)
list(APPEND LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES core/paths/paths-apple.h)
elseif (ANDROID)
list(APPEND LINPHONE_CXX_OBJECTS_SOURCE_FILES core/paths/paths-android.cpp core/platform-helpers/android-platform-helpers.cpp)
list(APPEND LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES core/paths/paths-android.h)
elseif (WIN32)
list(APPEND LINPHONE_CXX_OBJECTS_SOURCE_FILES core/paths/paths-windows.cpp)
list(APPEND LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES core/paths/paths-windows.h)
elseif (UNIX)
list(APPEND LINPHONE_CXX_OBJECTS_SOURCE_FILES core/paths/paths-linux.cpp)
list(APPEND LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES core/paths/paths-linux.h)
......@@ -293,6 +290,15 @@ set(LINPHONE_CXX_OBJECTS_INCLUDE_DIRS ${BELR_INCLUDE_DIRS})
if (SOCI_FOUND)
list(APPEND LINPHONE_CXX_OBJECTS_INCLUDE_DIRS ${SOCI_INCLUDE_DIRS} ${SOCI_MYSQL_INCLUDES})
add_definitions(-DSOCI_ENABLED)
list(APPEND LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES
db/internal/safe-transaction.h
db/internal/statements.h
)
list(APPEND LINPHONE_CXX_OBJECTS_SOURCE_FILES
db/internal/statements.cpp
)
endif ()
set(LINPHONE_PRIVATE_HEADER_FILES)
......
......@@ -69,14 +69,20 @@ public:
void setImdnMessageId (const std::string &imdnMessageId);
inline void forceFromAddress (const IdentityAddress &fromAddress) {
void forceFromAddress (const IdentityAddress &fromAddress) {
this->fromAddress = fromAddress;
}
inline void forceToAddress (const IdentityAddress &toAddress) {
void forceToAddress (const IdentityAddress &toAddress) {
this->toAddress = toAddress;
}
void markContentsAsNotLoaded () {
contentsNotLoadedFromDatabase = true;
}
void loadContentsFromDatabase () const;
belle_http_request_t *getHttpRequest () const;
void setHttpRequest (belle_http_request_t *request);
......@@ -142,7 +148,7 @@ private:
std::string imdnId;
std::string rttMessage;
bool isSecured = false;
bool isReadOnly = false;
mutable bool isReadOnly = false;
std::list<Content* > contents;
Content internalContent;
std::unordered_map<std::string, std::string> customHeaders;
......@@ -176,6 +182,7 @@ private:
bool encryptionPrevented = false;
bool toBeStored = true;
mutable bool contentsNotLoadedFromDatabase = false;
L_DECLARE_PUBLIC(ChatMessage);
};
......
......@@ -183,6 +183,7 @@ string ChatMessagePrivate::getSalCustomHeaderValue (const string &name) {
// -----------------------------------------------------------------------------
bool ChatMessagePrivate::hasTextContent() const {
loadContentsFromDatabase();
for (const Content *c : contents) {
if (c->getContentType() == ContentType::PlainText) {
return true;
......@@ -192,6 +193,7 @@ bool ChatMessagePrivate::hasTextContent() const {
}
const Content* ChatMessagePrivate::getTextContent() const {
loadContentsFromDatabase();
for (const Content *c : contents) {
if (c->getContentType() == ContentType::PlainText) {
return c;
......@@ -201,6 +203,7 @@ const Content* ChatMessagePrivate::getTextContent() const {
}
bool ChatMessagePrivate::hasFileTransferContent() const {
loadContentsFromDatabase();
for (const Content *c : contents) {
if (c->getContentType() == ContentType::FileTransfer) {
return true;
......@@ -227,6 +230,7 @@ void ChatMessagePrivate::setFileTransferFilepath (const string &path) {
}
const string &ChatMessagePrivate::getAppdata () const {
loadContentsFromDatabase();
for (const Content *c : contents) {
if (c->isFile()) {
FileContent *fileContent = (FileContent *)c;
......@@ -237,6 +241,7 @@ const string &ChatMessagePrivate::getAppdata () const {
}
void ChatMessagePrivate::setAppdata (const string &data) {
loadContentsFromDatabase();
for (const Content *c : contents) {
if (c->isFile()) {
FileContent *fileContent = (FileContent *)c;
......@@ -256,6 +261,7 @@ const string &ChatMessagePrivate::getExternalBodyUrl () const {
}
const ContentType &ChatMessagePrivate::getContentType () {
loadContentsFromDatabase();
if (direction == ChatMessage::Direction::Incoming) {
if (contents.size() > 0) {
Content *content = contents.front();
......@@ -277,6 +283,7 @@ const ContentType &ChatMessagePrivate::getContentType () {
}
void ChatMessagePrivate::setContentType (const ContentType &contentType) {
loadContentsFromDatabase();
if (contents.size() > 0 && internalContent.getContentType().isEmpty() && internalContent.isEmpty()) {
internalContent.setBody(contents.front()->getBody());
}
......@@ -290,6 +297,7 @@ void ChatMessagePrivate::setContentType (const ContentType &contentType) {
}
const string &ChatMessagePrivate::getText () {
loadContentsFromDatabase();
if (direction == ChatMessage::Direction::Incoming) {
if (hasTextContent()) {
cText = getTextContent()->getBodyAsString();
......@@ -313,6 +321,7 @@ const string &ChatMessagePrivate::getText () {
}
void ChatMessagePrivate::setText (const string &text) {
loadContentsFromDatabase();
if (contents.size() > 0 && internalContent.getContentType().isEmpty() && internalContent.isEmpty()) {
internalContent.setContentType(contents.front()->getContentType());
}
......@@ -356,6 +365,7 @@ void ChatMessagePrivate::setFileTransferInformation (const LinphoneContent *c_co
bool ChatMessagePrivate::downloadFile () {
L_Q();
loadContentsFromDatabase();
for (auto &content : contents)
if (content->getContentType() == ContentType::FileTransfer)
......@@ -887,6 +897,18 @@ void ChatMessagePrivate::setImdnMessageId (const string &id) {
imdnId = id;
}
void ChatMessagePrivate::loadContentsFromDatabase () const {
L_Q();
if (contentsNotLoadedFromDatabase) {
isReadOnly = false;
contentsNotLoadedFromDatabase = false;
q->getChatRoom()->getCore()->getPrivate()->mainDb->loadChatMessageContents(
const_pointer_cast<ChatMessage>(q->getSharedFromThis())
);
isReadOnly = true;
}
}
bool ChatMessage::isRead () const {
L_D();
......@@ -939,6 +961,7 @@ bool ChatMessage::isReadOnly () const {
const list<Content *> &ChatMessage::getContents () const {
L_D();
d->loadContentsFromDatabase();
return d->contents;
}
......@@ -1000,6 +1023,7 @@ void ChatMessage::send () {
return;
}
d->loadContentsFromDatabase();
getChatRoom()->getPrivate()->sendChatMessage(getSharedFromThis());
}
......@@ -1039,9 +1063,9 @@ void ChatMessage::cancelFileTransfer () {
int ChatMessage::putCharacter (uint32_t character) {
L_D();
static const uint32_t new_line = 0x2028;
static const uint32_t crlf = 0x0D0A;
static const uint32_t lf = 0x0A;
constexpr uint32_t newLine = 0x2028;
constexpr uint32_t crlf = 0x0D0A;
constexpr uint32_t lf = 0x0A;
shared_ptr<AbstractChatRoom> chatRoom = getChatRoom();
if (!(chatRoom->getCapabilities() & LinphonePrivate::ChatRoom::Capabilities::RealTimeText))
......@@ -1056,7 +1080,7 @@ int ChatMessage::putCharacter (uint32_t character) {
if (!call || !call->getPrivate()->getMediaStream(LinphoneStreamTypeText))
return -1;
if (character == new_line || character == crlf || character == lf) {
if (character == newLine || character == crlf || character == lf) {
shared_ptr<Core> core = getCore();
if (lp_config_get_int(core->getCCore()->config, "misc", "store_rtt_messages", 1) == 1) {
// TODO: History.
......
/*
* safe-transaction.h
* Copyright (C) 2010-2018 Belledonne Communications SARL
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _L_SAFE_TRANSACTION_H_
#define _L_SAFE_TRANSACTION_H_
#include <soci/soci.h>
#include "db/main-db.h"
#include "logger/logger.h"
// =============================================================================
#define L_SAFE_TRANSACTION_C(CONTEXT) \
LinphonePrivate::SafeTransactionInfo().set(__func__, CONTEXT) * [&]()
#define L_SAFE_TRANSACTION L_SAFE_TRANSACTION_C(this)
LINPHONE_BEGIN_NAMESPACE
struct SafeTransactionInfo {
SafeTransactionInfo &set (const char *_name, const MainDb *_mainDb) {
name = _name;
mainDb = const_cast<MainDb *>(_mainDb);
return *this;
}
const char *name = nullptr;
MainDb *mainDb = nullptr;
};
template<typename Function>
class SafeTransaction {
using InternalReturnType = typename std::remove_reference<decltype(std::declval<Function>()())>::type;
public:
using ReturnType = typename std::conditional<
std::is_same<InternalReturnType, void>::value,
bool,
InternalReturnType
>::type;
SafeTransaction (SafeTransactionInfo &info, Function function) : mFunction(std::move(function)) {
try {
mResult= exec<InternalReturnType>();
} catch (const soci::soci_error &e) {
lWarning() << "Catched exception in MainDb::" << info.name << "(" << e.what() << ").";
soci::soci_error::error_category category = e.get_error_category();
if (
(category == soci::soci_error::connection_error || category == soci::soci_error::unknown) &&
info.mainDb->forceReconnect()
) {
try {
mResult = exec<InternalReturnType>();
} catch (const std::exception &e) {
lError() << "Unable to execute query after reconnect in MainDb::" << info.name << "(" << e.what() << ").";
}
return;
}
lError() << "Unhandled [" << getErrorCategoryAsString(category) << "] exception in MainDb::" <<
info.name << ": `" << e.what() << "`.";
} catch (const std::exception &e) {
lError() << "Unhandled generic exception in MainDb::" << info.name << ": `" << e.what() << "`.";
}
}
SafeTransaction (SafeTransaction &&safeTransaction) : mFunction(std::move(safeTransaction.mFunction)) {}
operator ReturnType () const {
return mResult;
}
private:
// Exec function with no return type.
template<typename T>
typename std::enable_if<std::is_same<T, void>::value, bool>::type exec () const {
mFunction();
return true;
}
// Exec function with return type.
template<typename T>
typename std::enable_if<!std::is_same<T, void>::value, T>::type exec () const {
return mFunction();
}
static const char *getErrorCategoryAsString (soci::soci_error::error_category category) {
switch (category) {
case soci::soci_error::connection_error:
return "CONNECTION ERROR";
case soci::soci_error::invalid_statement:
return "INVALID STATEMENT";
case soci::soci_error::no_privilege:
return "NO PRIVILEGE";
case soci::soci_error::no_data:
return "NO DATA";
case soci::soci_error::constraint_violation:
return "CONSTRAINT VIOLATION";
case soci::soci_error::unknown_transaction_state:
return "UNKNOWN TRANSACTION STATE";
case soci::soci_error::system_error:
return "SYSTEM ERROR";
case soci::soci_error::unknown:
return "UNKNOWN";
}
// Unreachable.
L_ASSERT(false);
return nullptr;
}
Function mFunction;
ReturnType mResult{};
L_DISABLE_COPY(SafeTransaction);
};
template<typename Function>
typename SafeTransaction<Function>::ReturnType operator* (SafeTransactionInfo &info, Function &&function) {
return SafeTransaction<Function>(info, std::forward<Function>(function));
}
LINPHONE_END_NAMESPACE
#endif // ifndef _L_SAFE_TRANSACTION_H_
/*
* statements.cpp
* Copyright (C) 2010-2018 Belledonne Communications SARL
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "statements.h"
// =============================================================================
LINPHONE_BEGIN_NAMESPACE
namespace Statements {
// ---------------------------------------------------------------------------
// Create statements.
// ---------------------------------------------------------------------------
constexpr const char *create[CreateCount] = {
[CreateConferenceEventView] = R"(
CREATE TEMP VIEW conference_event_view AS
SELECT id, type, creation_time, chat_room_id, from_sip_address_id, to_sip_address_id, time, imdn_message_id, state, direction, is_secured, notify_id, device_sip_address_id, participant_sip_address_id, subject
FROM event
LEFT JOIN conference_event ON conference_event.event_id = event.id
LEFT JOIN conference_chat_message_event ON conference_chat_message_event.event_id = event.id
LEFT JOIN conference_notified_event ON conference_notified_event.event_id = event.id
LEFT JOIN conference_participant_device_event ON conference_participant_device_event.event_id = event.id
LEFT JOIN conference_participant_event ON conference_participant_event.event_id = event.id
LEFT JOIN conference_subject_event ON conference_subject_event.event_id = event.id
)"
};
// ---------------------------------------------------------------------------
// Select statements.
// ---------------------------------------------------------------------------
constexpr const char *select[SelectCount] = {
[SelectConferenceEvents] = R"(
SELECT conference_event_view.id AS event_id, type, creation_time, from_sip_address.value, to_sip_address.value, time, imdn_message_id, state, direction, is_secured, notify_id, device_sip_address.value, participant_sip_address.value, subject
FROM conference_event_view
LEFT JOIN sip_address AS from_sip_address ON from_sip_address.id = from_sip_address_id
LEFT JOIN sip_address AS to_sip_address ON to_sip_address.id = to_sip_address_id
LEFT JOIN sip_address AS device_sip_address ON device_sip_address.id = device_sip_address_id
LEFT JOIN sip_address AS participant_sip_address ON participant_sip_address.id = participant_sip_address_id
WHERE chat_room_id = :chatRoomId
)"
};
// ---------------------------------------------------------------------------
// Getters.
// ---------------------------------------------------------------------------
const char *get (Create createStmt, AbstractDb::Backend backend) {
(void)backend;
return createStmt >= Create::CreateCount ? nullptr : create[createStmt];
}
const char *get (Select selectStmt, AbstractDb::Backend backend) {
(void)backend;
return selectStmt >= Select::SelectCount ? nullptr : select[selectStmt];
}
}
LINPHONE_END_NAMESPACE
/*
* statements.h
* Copyright (C) 2010-2018 Belledonne Communications SARL
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _L_STATEMENTS_H_
#define _L_STATEMENTS_H_
#include "db/abstract/abstract-db.h"
// =============================================================================
LINPHONE_BEGIN_NAMESPACE
namespace Statements {
enum Create {
CreateConferenceEventView,
CreateCount
};
enum Select {
SelectConferenceEvents,
SelectCount
};
const char *get (Create createStmt, AbstractDb::Backend backend);
const char *get (Select selectStmt, AbstractDb::Backend backend);
}
LINPHONE_END_NAMESPACE
#endif // ifndef _L_STATEMENTS_H_
......@@ -103,6 +103,21 @@ private:
const ChatRoomId &chatRoomId
) const;
// TODO: Remove me. Workaround to increase fetch performance.
std::shared_ptr<EventLog> selectConferenceChatMessageEvent (
long long eventId,
EventLog::Type type,
time_t creationTime,
std::shared_ptr<AbstractChatRoom> &chatRoom,
const std::string &fromSipAddress,
const std::string &toSipAddress,
const tm &messageTime,
const std::string &imdnMessageId,
int state,
int direction,
int isSecured
) const;
std::shared_ptr<EventLog> selectConferenceParticipantEvent (
long long eventId,
EventLog::Type type,
......
This diff is collapsed.
......@@ -125,6 +125,12 @@ public:
void cleanHistory (const ChatRoomId &chatRoomId, FilterMask mask = NoFilter);
// ---------------------------------------------------------------------------
// Chat messages.
// ---------------------------------------------------------------------------
void loadChatMessageContents (const std::shared_ptr<ChatMessage> &chatMessage);
// ---------------------------------------------------------------------------
// Chat rooms.
// ---------------------------------------------------------------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment