db-exception-handler.h 4.37 KB
Newer Older
1
/*
2
 * db-exception-handler.h
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * 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.
 */

20 21
#ifndef _L_DB_EXCEPTION_HANDLER_H_
#define _L_DB_EXCEPTION_HANDLER_H_
22 23 24 25 26 27 28 29

#include <soci/soci.h>

#include "db/main-db.h"
#include "logger/logger.h"

// =============================================================================

30 31
#define L_DB_EXCEPTION_HANDLER_C(CONTEXT) \
	LinphonePrivate::DbExceptionHandlerInfo().set(__func__, CONTEXT) * [&]()
32

33
#define L_DB_EXCEPTION_HANDLER L_DB_EXCEPTION_HANDLER_C(this)
34 35 36

LINPHONE_BEGIN_NAMESPACE

37 38
struct DbExceptionHandlerInfo {
	DbExceptionHandlerInfo &set (const char *_name, const MainDb *_mainDb) {
39 40 41 42 43 44 45 46 47 48
		name = _name;
		mainDb = const_cast<MainDb *>(_mainDb);
		return *this;
	}

	const char *name = nullptr;
	MainDb *mainDb = nullptr;
};

template<typename Function>
49
class DbExceptionHandler {
50 51 52 53 54 55 56 57 58
	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;

59
	DbExceptionHandler (DbExceptionHandlerInfo &info, Function function) : mFunction(std::move(function)) {
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
		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() << "`.";
		}
	}

83
	DbExceptionHandler (DbExceptionHandler &&dbExceptionHandler) : mFunction(std::move(dbExceptionHandler.mFunction)) {}
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130

	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{};

131
	L_DISABLE_COPY(DbExceptionHandler);
132 133 134
};

template<typename Function>
135 136
typename DbExceptionHandler<Function>::ReturnType operator* (DbExceptionHandlerInfo &info, Function &&function) {
	return DbExceptionHandler<Function>(info, std::forward<Function>(function));
137 138 139 140
}

LINPHONE_END_NAMESPACE

141
#endif // ifndef _L_DB_EXCEPTION_HANDLER_H_