Commit 9072624a authored by Ronan's avatar Ronan

feat(AbstractDb): add a way to deal with exceptions

parent 8f245057
......@@ -21,6 +21,7 @@
#define _L_GENERAL_H_
#ifdef __cplusplus
#include <memory>
#include <type_traits>
#endif
......@@ -101,6 +102,12 @@ void l_assert (const char *condition, const char *file, int line);
// Define an integer version like: 0xXXYYZZ, XX=MAJOR, YY=MINOR, and ZZ=PATCH.
#define L_VERSION(MAJOR, MINOR, PATCH) (((MAJOR) << 16) | ((MINOR) << 8) | (PATCH))
// Not available in C++11...
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args && ...args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
// -----------------------------------------------------------------------------
// Data access.
// -----------------------------------------------------------------------------
......
......@@ -21,7 +21,6 @@
#define _L_UTILS_H_
#include <ctime>
#include <memory>
#include <string>
#include <vector>
......
......@@ -113,8 +113,6 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES
db/main-db-p.h
db/main-db.h
db/session/db-session-p.h
db/session/db-session-provider.h
db/session/db-session.h
dial-plan/dial-plan-p.h
dial-plan/dial-plan.h
enums.h
......@@ -222,7 +220,6 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES
db/main-db-event-key.cpp
db/main-db-key.cpp
db/main-db.cpp
db/session/db-session-provider.cpp
db/session/db-session.cpp
dial-plan/dial-plan.cpp
event-log/conference/conference-call-event.cpp
......
......@@ -20,8 +20,6 @@
#ifndef _L_CHAT_ROOM_LISTENER_H_
#define _L_CHAT_ROOM_LISTENER_H_
#include <memory>
#include "chat/chat-room/abstract-chat-room.h"
// =============================================================================
......
......@@ -21,7 +21,6 @@
#define _L_CPIM_GENERIC_HEADER_H_
#include <list>
#include <memory>
#include "cpim-header.h"
......
......@@ -21,7 +21,6 @@
#define _L_CONFERENCE_INTERFACE_H_
#include <list>
#include <memory>
#include "linphone/utils/general.h"
......
......@@ -20,8 +20,6 @@
#ifndef _L_LOCAL_CONFERENCE_EVENT_HANDLER_H_
#define _L_LOCAL_CONFERENCE_EVENT_HANDLER_H_
#include <memory>
#include "linphone/types.h"
#include "address/address.h"
......
......@@ -20,8 +20,6 @@
#ifndef _L_CALL_SESSION_PARAMS_H_
#define _L_CALL_SESSION_PARAMS_H_
#include <memory>
#include "object/clonable-object.h"
#include "linphone/types.h"
......
......@@ -20,8 +20,6 @@
#ifndef _L_MEDIA_SESSION_PARAMS_P_H_
#define _L_MEDIA_SESSION_PARAMS_P_H_
#include <memory>
#include "call-session-params-p.h"
#include "media-session-params.h"
......
......@@ -20,7 +20,6 @@
#ifndef _L_PARTICIPANT_DEVICE_H_
#define _L_PARTICIPANT_DEVICE_H_
#include <memory>
#include <string>
#include "address/identity-address.h"
......
......@@ -20,8 +20,6 @@
#ifndef _L_PARTICIPANT_P_H_
#define _L_PARTICIPANT_P_H_
#include <memory>
#include "object/object-p.h"
#include "conference/participant.h"
......
......@@ -20,8 +20,6 @@
#ifndef _L_CORE_ACCESSOR_H_
#define _L_CORE_ACCESSOR_H_
#include <memory>
#include "linphone/utils/general.h"
// =============================================================================
......
......@@ -17,18 +17,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef SOCI_ENABLED
#include <soci/soci.h>
#endif // ifdef SOCI_ENABLED
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif // ifdef __APPLE__
#include "linphone/utils/utils.h"
#include "abstract-db-p.h"
#include "db/session/db-session-provider.h"
#include "logger/logger.h"
// =============================================================================
......@@ -40,28 +33,30 @@ LINPHONE_BEGIN_NAMESPACE
AbstractDb::AbstractDb (AbstractDbPrivate &p) : Object(p) {}
// Force static sqlite3 linking for IOS and Android.
#if TARGET_OS_IPHONE || defined(__ANDROID__)
#if defined(SOCI_ENABLED) && (TARGET_OS_IPHONE || defined(__ANDROID__))
extern "C" void register_factory_sqlite3();
#endif // TARGET_OS_IPHONE || defined(__ANDROID__)
#endif // if defined(SOCI_ENABLED) && (TARGET_OS_IPHONE || defined(__ANDROID__))
bool AbstractDb::connect (Backend backend, const string &parameters) {
L_D();
#if TARGET_OS_IPHONE || defined(__ANDROID__)
#if defined(SOCI_ENABLED) && (TARGET_OS_IPHONE || defined(__ANDROID__))
if (backend == Sqlite3)
register_factory_sqlite3();
#endif // defined(TARGET_OS_IPHONE) || defined(__ANDROID__)
#endif // if defined(SOCI_ENABLED) && (TARGET_OS_IPHONE || defined(__ANDROID__))
d->backend = backend;
d->dbSession = DbSessionProvider::getInstance()->getSession(
d->dbSession = DbSession(
(backend == Mysql ? "mysql://" : "sqlite3://") + parameters
);
if (d->dbSession) {
try {
enableForeignKeys(false);
init();
enableForeignKeys(true);
#ifdef SOCI_ENABLED
d->dbSession.enableForeignKeys(false);
init();
d->dbSession.enableForeignKeys(true);
#endif // ifdef SOCI_ENABLED
} catch (const exception &e) {
lWarning() << "Unable to init database: " << e.what();
......@@ -73,154 +68,61 @@ bool AbstractDb::connect (Backend backend, const string &parameters) {
return d->dbSession;
}
bool AbstractDb::isConnected () const {
L_D();
return d->dbSession;
}
AbstractDb::Backend AbstractDb::getBackend () const {
L_D();
return d->backend;
}
bool AbstractDb::import (Backend, const string &) {
return false;
}
// -----------------------------------------------------------------------------
void AbstractDb::init () {
// Nothing.
}
// -----------------------------------------------------------------------------
string AbstractDb::primaryKeyStr (const string &type) const {
void AbstractDb::disconnect () {
L_D();
switch (d->backend) {
case Mysql:
return " " + type + " AUTO_INCREMENT PRIMARY KEY";
case Sqlite3:
// See: ROWIDs and the INTEGER PRIMARY KEY
// https://www.sqlite.org/lang_createtable.html
return " INTEGER PRIMARY KEY ASC";
}
L_ASSERT(false);
return "";
d->dbSession = DbSession();
}
string AbstractDb::primaryKeyRefStr (const string &type) const {
bool AbstractDb::forceReconnect () {
L_D();
switch (d->backend) {
case Mysql:
return " " + type;
case Sqlite3:
return " INTEGER";
}
L_ASSERT(false);
return "";
}
string AbstractDb::varcharPrimaryKeyStr (int length) const {
L_D();
switch (d->backend) {
case Mysql:
return " VARCHAR(" + Utils::toString(length) + ") PRIMARY KEY";
case Sqlite3:
return " VARCHAR(" + Utils::toString(length) + ") PRIMARY KEY";
if (!d->dbSession) {
lWarning() << "Unable to reconnect. Not a valid database session.";
return false;
}
L_ASSERT(false);
return "";
}
#ifdef SOCI_ENABLED
constexpr int retryCount = 2;
lInfo() << "Trying sql backend reconnect...";
string AbstractDb::timestampType () const {
L_D();
try {
soci::session *session = d->dbSession.getBackendSession();
session->close();
for (int i = 0; i < retryCount; ++i) {
try {
lInfo() << "Reconnect... Try: " << i;
session->reconnect();
lInfo() << "Database reconnection successful!";
return true;
} catch (const soci::soci_error &e) {
if (e.get_error_category() != soci::soci_error::connection_error)
throw e;
}
}
} catch (const exception &e) {
lError() << "Unable to reconnect: `" << e.what() << "`.";
return false;
}
switch (d->backend) {
case Mysql:
return " TIMESTAMP";
case Sqlite3:
return " DATE";
}
lError() << "Database reconnection failed!";
#endif // ifdef SOCI_ENABLED
L_ASSERT(false);
return "";
return false;
}
string AbstractDb::noLimitValue () const {
AbstractDb::Backend AbstractDb::getBackend () const {
L_D();
switch (d->backend) {
case Mysql:
return "9999999999999999999";
case Sqlite3:
return "-1";
}
L_ASSERT(false);
return "";
}
long long AbstractDb::getLastInsertId () const {
long long id = 0;
#ifdef SOCI_ENABLED
L_D();
string sql;
switch (d->backend) {
case Mysql:
sql = "SELECT LAST_INSERT_ID()";
break;
case Sqlite3:
sql = "SELECT last_insert_rowid()";
break;
}
soci::session *session = d->dbSession.getBackendSession<soci::session>();
*session << sql, soci::into(id);
#endif // ifdef SOCI_ENABLED
return id;
return d->backend;
}
void AbstractDb::enableForeignKeys (bool status) {
#ifdef SOCI_ENABLED
L_D();
soci::session *session = d->dbSession.getBackendSession<soci::session>();
switch (d->backend) {
case Mysql:
*session << string("SET FOREIGN_KEY_CHECKS = ") + (status ? "1" : "0");
break;
case Sqlite3:
*session << string("PRAGMA foreign_keys = ") + (status ? "ON" : "OFF");
break;
}
#endif // ifdef SOCI_ENABLED
bool AbstractDb::import (Backend, const string &) {
return false;
}
bool AbstractDb::checkTableExists (const string &table) const {
#ifdef SOCI_ENABLED
L_D();
soci::session *session = d->dbSession.getBackendSession<soci::session>();
switch (d->backend) {
case Mysql:
*session << "SHOW TABLES LIKE :table", soci::use(table);
return session->got_data() > 0;
case Sqlite3:
*session << "SELECT name FROM sqlite_master WHERE type='table' AND name=:table", soci::use(table);
return session->got_data() > 0;
}
#endif // ifdef SOCI_ENABLED
// -----------------------------------------------------------------------------
L_ASSERT(false);
return false;
void AbstractDb::init () {
// Nothing.
}
LINPHONE_END_NAMESPACE
......@@ -38,9 +38,9 @@ public:
virtual ~AbstractDb () = default;
bool connect (Backend backend, const std::string &parameters);
bool disconnect ();
void disconnect ();
bool isConnected () const;
bool forceReconnect ();
Backend getBackend () const;
......@@ -51,20 +51,6 @@ protected:
virtual void init ();
std::string primaryKeyStr (const std::string &type = "INT") const;
std::string primaryKeyRefStr (const std::string &type = "INT") const;
std::string varcharPrimaryKeyStr (int length) const;
std::string timestampType () const;
std::string noLimitValue () const;
long long getLastInsertId () const;
void enableForeignKeys (bool status);
bool checkTableExists (const std::string &table) const;
private:
L_DECLARE_PRIVATE(AbstractDb);
L_DISABLE_COPY(AbstractDb);
......
......@@ -20,8 +20,6 @@
#ifndef _L_MAIN_DB_KEY_H_
#define _L_MAIN_DB_KEY_H_
#include <memory>
#include "object/clonable-object.h"
// =============================================================================
......
......@@ -20,6 +20,8 @@
#ifndef _L_MAIN_DB_P_H_
#define _L_MAIN_DB_P_H_
#include <unordered_map>
#include "abstract/abstract-db-p.h"
#include "event-log/event-log.h"
#include "main-db.h"
......
This diff is collapsed.
......@@ -20,8 +20,6 @@
#ifndef _L_DB_SESSION_P_H_
#define _L_DB_SESSION_P_H_
#include <memory>
#include "db-session.h"
#include "object/clonable-object-p.h"
......@@ -33,10 +31,15 @@ class DbSessionPrivate : public ClonableObjectPrivate {
friend class DbSessionProvider;
private:
bool isValid = false;
DbSession::Type type = DbSession::None;
std::shared_ptr<void> backendSession;
enum class Backend {
None,
Mysql,
Sqlite3
} backend;
#ifdef SOCI_ENABLED
std::unique_ptr<soci::session> backendSession;
#endif // ifdef SOCI_ENABLED
L_DECLARE_PUBLIC(DbSession);
};
......
/*
* db-session-provider.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.
*/
#ifdef SOCI_ENABLED
#include <soci/soci.h>
#endif // ifdef SOCI_ENABLED
#include "db-session-p.h"
#include "logger/logger.h"
#include "object/object-p.h"
#include "db-session-provider.h"
// =============================================================================
using namespace std;
LINPHONE_BEGIN_NAMESPACE
DbSessionProvider::DbSessionProvider () : Singleton(*new ObjectPrivate) {}
DbSession DbSessionProvider::getSession (const string &uri) {
DbSession session(DbSession::None);
#ifdef SOCI_ENABLED
try {
session = DbSession(DbSession::Soci);
DbSessionPrivate *p = session.getPrivate();
p->backendSession = make_shared<soci::session>(uri);
p->isValid = true;
} catch (const exception &e) {
session = DbSession(DbSession::None);
lWarning() << "Unable to get db session: " << e.what();
}
#else
lWarning() << "Unable to get db session: soci not enabled.";
#endif // ifdef SOCI_ENABLED
return session;
}
LINPHONE_END_NAMESPACE
/*
* db-session-provider.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_DB_SESSION_PROVIDER_H_
#define _L_DB_SESSION_PROVIDER_H_
#include "db-session.h"
#include "object/singleton.h"
// =============================================================================
LINPHONE_BEGIN_NAMESPACE
class DbSessionProviderPrivate;
class DbSessionProvider : public Singleton<DbSessionProvider> {
friend class Singleton<DbSessionProvider>;
public:
DbSession getSession (const std::string &uri);
private:
DbSessionProvider ();
L_DISABLE_COPY(DbSessionProvider);
};
LINPHONE_END_NAMESPACE
#endif // ifndef _L_DB_SESSION_PROVIDER_H_
......@@ -17,7 +17,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "linphone/utils/utils.h"
#include "db-session-p.h"
#include "logger/logger.h"
// =============================================================================
......@@ -25,26 +28,176 @@ using namespace std;
LINPHONE_BEGIN_NAMESPACE
DbSession::DbSession (Type type) : ClonableObject(*new DbSessionPrivate) {
L_D();
d->type = type;
DbSession::DbSession () : ClonableObject(*new DbSessionPrivate) {}
DbSession::DbSession (const string &uri) : DbSession() {
#ifdef SOCI_ENABLED
try {
L_D();
d->backendSession = make_unique<soci::session>(uri);
d->backend = !uri.find("mysql") ? DbSessionPrivate::Backend::Mysql : DbSessionPrivate::Backend::Sqlite3;
} catch (const exception &e) {
lWarning() << "Unable to build db session with uri: " << e.what();
}
#else
lWarning() << "Unable to build db session with uri: soci not enabled.";
#endif // ifdef SOCI_ENABLED
}
DbSession::operator bool () const {
L_D();
return d->isValid;
return d->backend != DbSessionPrivate::Backend::None;
}
L_USE_DEFAULT_CLONABLE_OBJECT_SHARED_IMPL(DbSession);
DbSession::Type DbSession::getBackendType () const {
#ifdef SOCI_ENABLED
soci::session *DbSession::getBackendSession () const {
L_D();
return d->backendSession.get();
}
#endif // ifdef SOCI_ENABLED
string DbSession::primaryKeyStr (const string &type) const {
L_D();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
return " " + type + " AUTO_INCREMENT PRIMARY KEY";
case DbSessionPrivate::Backend::Sqlite3:
// See: ROWIDs and the INTEGER PRIMARY KEY
// https://www.sqlite.org/lang_createtable.html
return " INTEGER PRIMARY KEY ASC";
case DbSessionPrivate::Backend::None:
return "";
}
L_ASSERT(false);
return "";
}
string DbSession::primaryKeyRefStr (const string &type) const {
L_D();
return d->type;
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
return " " + type;
case DbSessionPrivate::Backend::Sqlite3:
return " INTEGER";
case DbSessionPrivate::Backend::None:
return "";
}
L_ASSERT(false);
return "";
}
void *DbSession::getBackendSession () const {
string DbSession::varcharPrimaryKeyStr (int length) const {
L_D();
return d->backendSession.get();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
return " VARCHAR(" + Utils::toString(length) + ") PRIMARY KEY";
case DbSessionPrivate::Backend::Sqlite3:
return " VARCHAR(" + Utils::toString(length) + ") PRIMARY KEY";
case DbSessionPrivate::Backend::None:
return "";
}
L_ASSERT(false);
return "";
}
string DbSession::timestampType () const {
L_D();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
return " TIMESTAMP";
case DbSessionPrivate::Backend::Sqlite3:
return " DATE";
case DbSessionPrivate::Backend::None:
return "";
}
L_ASSERT(false);
return "";
}
string DbSession::noLimitValue () const {
L_D();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
return "9999999999999999999";
case DbSessionPrivate::Backend::Sqlite3:
return "-1";
case DbSessionPrivate::Backend::None:
return "";
}
L_ASSERT(false);
return "";
}
long long DbSession::getLastInsertId () const {
long long id = 0;
L_D();
string sql;
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
sql = "SELECT LAST_INSERT_ID()";
break;
case DbSessionPrivate::Backend::Sqlite3:
sql = "SELECT last_insert_rowid()";
break;
case DbSessionPrivate::Backend::None:
break;
}
#ifdef SOCI_ENABLED
*d->backendSession << sql, soci::into(id);
#endif // ifdef SOCI_ENABLED
return id;
}
void DbSession::enableForeignKeys (bool status) {
#ifdef SOCI_ENABLED
L_D();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
*d->backendSession << string("SET FOREIGN_KEY_CHECKS = ") + (status ? "1" : "0");