Commit d60b5fab authored by Ronan's avatar Ronan

feat(EventsDb): import correctly legacy messages

parent 32e22abe
......@@ -117,9 +117,11 @@ constexpr T *getPublicHelper (Object *object, const ObjectPrivate *) {
#define L_DECLARE_PUBLIC(CLASS) \
inline CLASS *getPublic () { \
L_ASSERT(mPublic); \
return getPublicHelper<CLASS>(mPublic, this); \
} \
inline const CLASS *getPublic () const { \
L_ASSERT(mPublic); \
return getPublicHelper<const CLASS>(mPublic, this); \
} \
friend class CLASS;
......
......@@ -72,9 +72,11 @@ Content &Content::operator= (Content &&src) {
return *this;
}
bool Content::operator==(const Content& content) const {
bool Content::operator== (const Content &content) const {
L_D();
return d->contentType == content.getContentType() && d->body == content.getBody() && d->contentDisposition == content.getContentDisposition();
return d->contentType == content.getContentType() &&
d->body == content.getBody() &&
d->contentDisposition == content.getContentDisposition();
}
const ContentType &Content::getContentType () const {
......
......@@ -39,7 +39,7 @@ public:
Content &operator= (const Content &src);
Content &operator= (Content &&src);
bool operator==(const Content& content) const;
bool operator== (const Content &content) const;
const ContentType &getContentType () const;
void setContentType (const ContentType &contentType);
......
......@@ -17,6 +17,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef SOCI_ENABLED
#include <soci/soci.h>
#endif // ifdef SOCI_ENABLED
#include "abstract-db-p.h"
#include "db/provider/db-session-provider.h"
#include "logger/logger.h"
......@@ -86,17 +90,27 @@ string AbstractDb::primaryKeyAutoIncrementStr (const string &type) const {
return "";
}
string AbstractDb::insertOrIgnoreStr () const {
long AbstractDb::getLastInsertId () const {
L_D();
switch (d->backend) {
case Mysql:
return "INSERT IGNORE INTO ";
string sql;
switch(d->backend) {
case Sqlite3:
return "INSERT OR IGNORE INTO ";
sql = "SELECT last_insert_rowid()";
break;
default:
lWarning() << "Unsupported backend.";
return -1;
}
return "";
long result = 0;
#ifdef SOCI_ENABLED
soci::session *session = d->dbSession.getBackendSession<soci::session>();
*session << sql, soci::into(result);
#endif // ifdef SOCI_ENABLED
return result;
}
LINPHONE_END_NAMESPACE
......@@ -52,7 +52,8 @@ protected:
virtual void init ();
std::string primaryKeyAutoIncrementStr (const std::string &type = "INT") const;
std::string insertOrIgnoreStr () const;
long getLastInsertId () const;
private:
L_DECLARE_PRIVATE(AbstractDb);
......
......@@ -41,7 +41,38 @@ using namespace std;
LINPHONE_BEGIN_NAMESPACE
class EventsDbPrivate : public AbstractDbPrivate {};
struct MessageEventReferences {
#ifdef SOCI_ENABLED
long eventId;
long localSipAddressId;
long remoteSipAddressId;
long chatRoomId;
long contentTypeId;
#endif
};
class EventsDbPrivate : public AbstractDbPrivate {
#ifdef SOCI_ENABLED
public:
long insertSipAddress (const string &sipAddress);
long insertContentType (const string &contentType);
long insertEvent (EventLog::Type type, const tm &date);
long insertChatRoom (long sipAddressId, const tm &date);
long insertMessageEvent (
const MessageEventReferences &references,
ChatMessage::State state,
ChatMessage::Direction direction,
const string &imdnMessageId,
bool isSecured,
const string *text = nullptr
);
void importLegacyMessages (const soci::rowset<soci::row> &messages);
#endif
private:
L_DECLARE_PUBLIC(EventsDb);
};
// -----------------------------------------------------------------------------
......@@ -78,16 +109,6 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {}
);
}
// -----------------------------------------------------------------------------
struct MessageEventReferences {
long eventId;
long localSipAddressId;
long remoteSipAddressId;
long chatRoomId;
long contentTypeId;
};
// -----------------------------------------------------------------------------
static string buildSqlEventFilter (const list<EventsDb::Filter> &filters, EventsDb::FilterMask mask) {
......@@ -111,7 +132,7 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {}
sql += " WHERE ";
} else
sql += " OR ";
sql += " event_type_id = ";
sql += " type = ";
sql += mapEventFilterToSql(filter);
}
......@@ -120,73 +141,148 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {}
// -----------------------------------------------------------------------------
static inline long insertSipAddress (soci::session &session, const string &sipAddress) {
long EventsDbPrivate::insertSipAddress (const string &sipAddress) {
L_Q();
soci::session *session = dbSession.getBackendSession<soci::session>();
long id;
session << "SELECT id FROM sip_address WHERE value = :sipAddress", soci::use(sipAddress), soci::into(id);
if (session.got_data())
*session << "SELECT id FROM sip_address WHERE value = :sipAddress", soci::use(sipAddress), soci::into(id);
if (session->got_data())
return id;
session << "INSERT INTO sip_address (value) VALUES (:sipAddress)", soci::use(sipAddress);
session.get_last_insert_id("sip_address", id);
return id;
*session << "INSERT INTO sip_address (value) VALUES (:sipAddress)", soci::use(sipAddress);
return q->getLastInsertId();
}
static inline long insertContentType (soci::session &session, const string &contentType) {
long EventsDbPrivate::insertContentType (const string &contentType) {
L_Q();
soci::session *session = dbSession.getBackendSession<soci::session>();
long id;
session << "SELECT id FROM content_type WHERE value = :contentType", soci::use(contentType), soci::into(id);
if (session.got_data())
*session << "SELECT id FROM content_type WHERE value = :contentType", soci::use(contentType), soci::into(id);
if (session->got_data())
return id;
session << "INSERT INTO content_type (value) VALUES (:contentType)", soci::use(contentType);
session.get_last_insert_id("content_type", id);
return id;
*session << "INSERT INTO content_type (value) VALUES (:contentType)", soci::use(contentType);
return q->getLastInsertId();
}
static inline long insertEvent (soci::session &session, EventLog::Type type, const tm &date) {
session << "INSERT INTO event (event_type_id, date) VALUES (:eventTypeId, :date)",
long EventsDbPrivate::insertEvent (EventLog::Type type, const tm &date) {
L_Q();
soci::session *session = dbSession.getBackendSession<soci::session>();
*session << "INSERT INTO event (type, date) VALUES (:type, :date)",
soci::use(static_cast<int>(type)), soci::use(date);
long id;
session.get_last_insert_id("event", id);
return id;
return q->getLastInsertId();
}
static inline long insertChatRoom (soci::session &session, long sipAddressId, const tm &date) {
long EventsDbPrivate::insertChatRoom (long sipAddressId, const tm &date) {
soci::session *session = dbSession.getBackendSession<soci::session>();
long id;
session << "SELECT peer_sip_address_id FROM chat_room WHERE peer_sip_address_id = :sipAddressId",
*session << "SELECT peer_sip_address_id FROM chat_room WHERE peer_sip_address_id = :sipAddressId",
soci::use(sipAddressId), soci::into(id);
if (!session.got_data())
session << "INSERT INTO chat_room (peer_sip_address_id, creation_date, last_update_date, subject) VALUES"
if (!session->got_data())
*session << "INSERT INTO chat_room (peer_sip_address_id, creation_date, last_update_date, subject) VALUES"
" (:sipAddressId, :creationDate, :lastUpdateDate, '')", soci::use(sipAddressId), soci::use(date), soci::use(date);
else
session << "UPDATE chat_room SET last_update_date = :lastUpdateDate WHERE peer_sip_address_id = :sipAddressId",
*session << "UPDATE chat_room SET last_update_date = :lastUpdateDate WHERE peer_sip_address_id = :sipAddressId",
soci::use(date), soci::use(sipAddressId);
return sipAddressId;
}
static inline long insertMessageEvent (
soci::session &session,
long EventsDbPrivate::insertMessageEvent (
const MessageEventReferences &references,
ChatMessage::State state,
ChatMessage::Direction direction,
const string &imdnMessageId,
bool isSecured,
const string *text = nullptr
const string *text
) {
soci::indicator textIndicator = text ? soci::i_ok : soci::i_null;
L_Q();
soci::session *session = dbSession.getBackendSession<soci::session>();
session << "INSERT INTO message_event ("
" event_id, chat_room_id, local_sip_address_id, remote_sip_address_id, content_type_id,"
soci::indicator textIndicator = text ? soci::i_ok : soci::i_null;
*session << "INSERT INTO message_event ("
" event_id, chat_room_id, local_sip_address_id, remote_sip_address_id,"
" state, direction, imdn_message_id, is_secured, text"
") VALUES ("
" :eventId, :chatRoomId, :localSipaddressId, :remoteSipaddressId, :contentTypeId,"
" :eventId, :chatRoomId, :localSipaddressId, :remoteSipaddressId,"
" :state, :direction, :imdnMessageId, :isSecured, :text"
")", soci::use(references.eventId), soci::use(references.chatRoomId), soci::use(references.localSipAddressId),
soci::use(references.remoteSipAddressId), soci::use(references.contentTypeId),
soci::use(static_cast<int>(state)), soci::use(static_cast<int>(direction)), soci::use(imdnMessageId),
soci::use(isSecured ? 1 : 0), soci::use(text ? *text : string(), textIndicator);
long id;
return session.get_last_insert_id("message_event", id);
return id;
soci::use(references.remoteSipAddressId), soci::use(static_cast<int>(state)),
soci::use(static_cast<int>(direction)), soci::use(imdnMessageId), soci::use(isSecured ? 1 : 0),
soci::use(text ? *text : string(), textIndicator);
return q->getLastInsertId();
}
// -----------------------------------------------------------------------------
template<typename T>
static T getValueFromLegacyMessage (const soci::row &message, int index, bool &isNull) {
isNull = false;
try {
return message.get<T>(index);
} catch (const exception &) {
isNull = true;
}
return T();
}
void EventsDbPrivate::importLegacyMessages (const soci::rowset<soci::row> &messages) {
soci::session *session = dbSession.getBackendSession<soci::session>();
soci::transaction tr(*session);
for (const auto &message : messages) {
const int direction = message.get<int>(3) + 1;
if (direction != 1 && direction != 2) {
lWarning() << "Unable to import legacy message with invalid direction.";
return;
}
const int state = message.get<int>(7, static_cast<int>(ChatMessage::State::Displayed));
const tm date = Utils::getLongAsTm(message.get<int>(9, 0));
const bool noUrl = false;
const string url = getValueFromLegacyMessage<string>(message, 8, const_cast<bool &>(noUrl));
const string contentType = message.get<string>(
13,
message.get<int>(11, -1) != -1
? "application/vnd.gsma.rcs-ft-http+xml"
: (noUrl ? "text/plain" : "message/external-body")
);
const bool noText = false;
const string text = getValueFromLegacyMessage<string>(message, 4, const_cast<bool &>(noText));
struct MessageEventReferences references;
references.eventId = insertEvent(EventLog::Type::ChatMessage, date);
references.localSipAddressId = insertSipAddress(message.get<string>(1));
references.remoteSipAddressId = insertSipAddress(message.get<string>(2));
references.chatRoomId = insertChatRoom(references.remoteSipAddressId, date);
references.contentTypeId = insertContentType(contentType);
insertMessageEvent (
references,
static_cast<ChatMessage::State>(state),
static_cast<ChatMessage::Direction>(direction),
message.get<string>(12, ""),
!!message.get<int>(14, 0),
noText ? nullptr : &text
);
const bool noAppData = false;
const string appData = getValueFromLegacyMessage<string>(message, 10, const_cast<bool &>(noAppData));
(void)appData;
}
tr.commit();
}
// -----------------------------------------------------------------------------
......@@ -210,7 +306,7 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {}
*session <<
"CREATE TABLE IF NOT EXISTS event ("
" id" + primaryKeyAutoIncrementStr() + ","
" event_type_id TINYINT UNSIGNED NOT NULL,"
" type TINYINT UNSIGNED NOT NULL,"
" date DATE NOT NULL"
")";
......@@ -238,10 +334,8 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {}
" id" + primaryKeyAutoIncrementStr() + ","
" event_id INT UNSIGNED NOT NULL,"
" chat_room_id INT UNSIGNED NOT NULL,"
" local_sip_address_id INT UNSIGNED NOT NULL,"
" remote_sip_address_id INT UNSIGNED NOT NULL,"
" content_type_id INT UNSIGNED NOT NULL,"
// See: https://tools.ietf.org/html/rfc5438#section-6.3
......@@ -468,79 +562,14 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {}
// -----------------------------------------------------------------------------
template<typename T>
static T getValueFromLegacyMessage (const soci::row &message, int index, bool &isNull) {
isNull = false;
try {
return message.get<T>(index);
} catch (const exception &) {
isNull = true;
}
return T();
}
static void importLegacyMessages (
soci::session *session,
const string &insertOrIgnoreStr,
const soci::rowset<soci::row> &messages
) {
soci::transaction tr(*session);
for (const auto &message : messages) {
const int direction = message.get<int>(3) + 1;
if (direction != 1 && direction != 2) {
lWarning() << "Unable to import legacy message with invalid direction.";
return;
}
const int state = message.get<int>(7, static_cast<int>(ChatMessage::State::Displayed));
const tm date = Utils::getLongAsTm(message.get<int>(9, 0));
const bool noUrl = false;
const string url = getValueFromLegacyMessage<string>(message, 8, const_cast<bool &>(noUrl));
const string contentType = message.get<string>(
13,
message.get<int>(11, -1) != -1
? "application/vnd.gsma.rcs-ft-http+xml"
: (noUrl ? "text/plain" : "message/external-body")
);
const bool noText = false;
const string text = getValueFromLegacyMessage<string>(message, 4, const_cast<bool &>(noText));
struct MessageEventReferences references;
references.eventId = insertEvent(*session, EventLog::Type::ChatMessage, date);
references.localSipAddressId = insertSipAddress(*session, message.get<string>(1));
references.remoteSipAddressId = insertSipAddress(*session, message.get<string>(2));
references.chatRoomId = insertChatRoom(*session, references.remoteSipAddressId, date);
references.contentTypeId = insertContentType(*session, contentType);
insertMessageEvent (
*session,
references,
static_cast<ChatMessage::State>(state),
static_cast<ChatMessage::Direction>(direction),
message.get<string>(12, ""),
!!message.get<int>(14, 0),
noText ? nullptr : &text
);
const bool noAppData = false;
const string appData = getValueFromLegacyMessage<string>(message, 10, const_cast<bool &>(noAppData));
(void)text;
(void)appData;
}
tr.commit();
}
bool EventsDb::import (Backend, const string &parameters) {
L_D();
if (!isConnected()) {
lWarning() << "Unable to import data. Not connected.";
return 0;
}
// Backend is useless, it's sqlite3. (Only available legacy backend.)
const string uri = "sqlite3://" + parameters;
DbSession inDbSession = DbSessionProvider::getInstance()->getSession(uri);
......@@ -550,14 +579,13 @@ EventsDb::EventsDb () : AbstractDb(*new EventsDbPrivate) {}
return false;
}
soci::session *outSession = d->dbSession.getBackendSession<soci::session>();
soci::session *inSession = inDbSession.getBackendSession<soci::session>();
// Import messages.
try {
soci::rowset<soci::row> messages = (inSession->prepare << "SELECT * FROM history ORDER BY id DESC");
soci::rowset<soci::row> messages = (inSession->prepare << "SELECT * FROM history");
try {
importLegacyMessages(outSession, insertOrIgnoreStr(), messages);
d->importLegacyMessages(messages);
} catch (const exception &e) {
lInfo() << "Failed to import legacy messages from: `" << uri << "`. (" << e.what() << ")";
return false;
......
......@@ -36,7 +36,7 @@ public:
virtual ~ObjectPrivate () = default;
protected:
Object *mPublic;
Object *mPublic = nullptr;
private:
std::unordered_map<std::string, Variant> properties;
......
......@@ -30,12 +30,14 @@ using namespace std;
LINPHONE_BEGIN_NAMESPACE
Object::Object (ObjectPrivate &p) : mPrivate(&p) {
mPrivate->mPublic = this;
}
Object::~Object () {
delete mPrivate;
}
Object::Object (ObjectPrivate &p) : mPrivate(&p) {}
shared_ptr<Object> Object::getSharedFromThis () {
return const_pointer_cast<Object>(static_cast<const Object *>(this)->getSharedFromThis());
}
......
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