From 07e0d4f90cb6435e4ffe3c02864b98fe4df78c14 Mon Sep 17 00:00:00 2001
From: Andrey Volk <andywolk@gmail.com>
Date: Mon, 2 Apr 2018 14:02:19 +0300
Subject: [PATCH] FS-10801: [core] Add a database interface to the FreeSWITCH
 Core.

---
 src/include/private/switch_core_pvt.h  |   3 +
 src/include/switch_core.h              |  27 +-
 src/include/switch_loadable_module.h   |  25 ++
 src/include/switch_module_interfaces.h |  33 ++
 src/include/switch_types.h             |   4 +
 src/switch_core.c                      |  35 +-
 src/switch_core_sqldb.c                | 396 ++++++++++++++++++--
 src/switch_loadable_module.c           | 497 +++++++++++++++++++++++--
 8 files changed, 943 insertions(+), 77 deletions(-)

diff --git a/src/include/private/switch_core_pvt.h b/src/include/private/switch_core_pvt.h
index 8f8076a5bf..6057734123 100644
--- a/src/include/private/switch_core_pvt.h
+++ b/src/include/private/switch_core_pvt.h
@@ -24,6 +24,7 @@
  * Contributor(s):
  *
  * Anthony Minessale II <anthm@freeswitch.org>
+ * Andrey Volk <andywolk@gmail.com>
  *
  *
  * switch_core.h -- Core Library Private Data (not to be installed into the system)
@@ -334,6 +335,8 @@ extern struct switch_session_manager session_manager;
 
 
 
+switch_status_t switch_core_sqldb_init(const char **err);
+void switch_core_sqldb_destroy();
 switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_t manage);
 void switch_core_sqldb_stop(void);
 void switch_core_session_init(switch_memory_pool_t *pool);
diff --git a/src/include/switch_core.h b/src/include/switch_core.h
index 7631fc287f..e82690dfef 100644
--- a/src/include/switch_core.h
+++ b/src/include/switch_core.h
@@ -27,6 +27,7 @@
  * Luke Dashjr <luke@openmethods.com> (OpenMethods, LLC)
  * Joseph Sullivan <jossulli@amazon.com>
  * Emmanuel Schmidbauer <eschmidbauer@gmail.com>
+ * Andrey Volk <andywolk@gmail.com>
  *
  * switch_core.h -- Core Library
  *
@@ -2474,13 +2475,15 @@ typedef enum {
 typedef enum {
 	SCDB_TYPE_CORE_DB,
 	SCDB_TYPE_ODBC,
-	SCDB_TYPE_PGSQL
+	SCDB_TYPE_PGSQL,
+	SCDB_TYPE_DATABASE_INTERFACE
 } switch_cache_db_handle_type_t;
 
 typedef union {
 	switch_core_db_t *core_db_dbh;
 	switch_odbc_handle_t *odbc_dbh;
 	switch_pgsql_handle_t *pgsql_dbh;
+	switch_database_interface_handle_t *database_interface_dbh;
 } switch_cache_db_native_handle_t;
 
 typedef struct {
@@ -2497,10 +2500,18 @@ typedef struct {
 	char *dsn;
 } switch_cache_db_pgsql_options_t;
 
+typedef struct {
+	char *dsn;
+	char prefix[16];
+	switch_database_interface_t *database_interface;
+	switch_bool_t make_module_no_unloadable;
+} switch_cache_db_database_interface_options_t;
+
 typedef union {
 	switch_cache_db_core_db_options_t core_db_options;
 	switch_cache_db_odbc_options_t odbc_options;
 	switch_cache_db_pgsql_options_t pgsql_options;
+	switch_cache_db_database_interface_options_t database_interface_options;
 } switch_cache_db_connection_options_t;
 
 struct switch_cache_db_handle;
@@ -2511,6 +2522,11 @@ static inline const char *switch_cache_db_type_name(switch_cache_db_handle_type_
 	const char *type_str = "INVALID";
 
 	switch (type) {
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			type_str = "DATABASE_INTERFACE";
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			type_str = "PGSQL";
@@ -2559,6 +2575,8 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h
 															   const char *file, const char *func, int line);
 #define switch_cache_db_get_db_handle(_a, _b, _c) _switch_cache_db_get_db_handle(_a, _b, _c, __FILE__, __SWITCH_FUNC__, __LINE__)
 
+SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle_dsn_ex(switch_cache_db_handle_t **dbh, const char *dsn, switch_bool_t make_module_no_unloadable,
+																	  const char *file, const char *func, int line);
 SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle_dsn(switch_cache_db_handle_t **dbh, const char *dsn,
 																   const char *file, const char *func, int line);
 #define switch_cache_db_get_db_handle_dsn(_a, _b) _switch_cache_db_get_db_handle_dsn(_a, _b, __FILE__, __SWITCH_FUNC__, __LINE__)
@@ -2643,6 +2661,13 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_persistant_execute_trans_full(sw
 																			  const char *inner_post_trans_execute);
 #define switch_cache_db_persistant_execute_trans(_d, _s, _r) switch_cache_db_persistant_execute_trans_full(_d, _s, _r, NULL, NULL, NULL, NULL)
 
+SWITCH_DECLARE(void) switch_cache_db_database_interface_flush_handles(switch_database_interface_t *database_interface);
+
+/*!
+\brief Returns error if no suitable database interface found to serve core db dsn.
+*/
+SWITCH_DECLARE(switch_status_t) switch_core_check_core_db_dsn();
+
 SWITCH_DECLARE(void) switch_core_set_signal_handlers(void);
 SWITCH_DECLARE(uint32_t) switch_core_debug_level(void);
 SWITCH_DECLARE(int32_t) switch_core_sps(void);
diff --git a/src/include/switch_loadable_module.h b/src/include/switch_loadable_module.h
index 87295a641b..be32c99e2e 100644
--- a/src/include/switch_loadable_module.h
+++ b/src/include/switch_loadable_module.h
@@ -24,6 +24,7 @@
  * Contributor(s):
  *
  * Anthony Minessale II <anthm@freeswitch.org>
+ * Andrey Volk <andywolk@gmail.com>
  *
  *
  * switch_loadable_module.h -- Loadable Modules
@@ -51,6 +52,14 @@ SWITCH_BEGIN_EXTERN_C
   \ingroup core1
   \{
 */
+
+/*! \brief List of loadable module types */
+	typedef enum {
+		SWITCH_LOADABLE_MODULE_TYPE_PRELOAD,
+		SWITCH_LOADABLE_MODULE_TYPE_COMMON,
+		SWITCH_LOADABLE_MODULE_TYPE_POSTLOAD
+	} switch_loadable_module_type_t;
+
 /*! \brief The abstraction of a loadable module */
 	struct switch_loadable_module_interface {
 	/*! the name of the module */
@@ -87,6 +96,8 @@ SWITCH_BEGIN_EXTERN_C
 	switch_management_interface_t *management_interface;
 	/*! the table of limit interfaces the module has implemented */
 	switch_limit_interface_t *limit_interface;
+	/*! the table of database interfaces the module has implemented */
+	switch_database_interface_t *database_interface;
 	switch_thread_rwlock_t *rwlock;
 	int refs;
 	switch_memory_pool_t *pool;
@@ -205,6 +216,13 @@ SWITCH_DECLARE(switch_json_api_interface_t *) switch_loadable_module_get_json_ap
  */
 SWITCH_DECLARE(switch_file_interface_t *) switch_loadable_module_get_file_interface(const char *name, const char *modname);
 
+/*!
+\brief Retrieve the database interface by it's registered name
+\param name the name of the dsn prefix
+\return the desired database format interface
+*/
+SWITCH_DECLARE(switch_database_interface_t *) switch_loadable_module_get_database_interface(const char *name, const char *modname);
+
 /*!
   \brief Retrieve the speech interface by it's registered name
   \param name the name of the speech interface
@@ -311,6 +329,13 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_load_module(const char *d
 */
 SWITCH_DECLARE(switch_status_t) switch_loadable_module_exists(const char *mod);
 
+/*!
+\brief Protect module from beeing unloaded
+\param mod the module name
+\return the status
+*/
+SWITCH_DECLARE(switch_status_t) switch_loadable_module_protect(const char *mod);
+
 /*!
   \brief Unoad a module
   \param dir the directory where the module resides
diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h
index 798695d090..54cd7b2de2 100644
--- a/src/include/switch_module_interfaces.h
+++ b/src/include/switch_module_interfaces.h
@@ -25,6 +25,7 @@
  *
  * Anthony Minessale II <anthm@freeswitch.org>
  * Luke Dashjr <luke@openmethods.com> (OpenMethods, LLC)
+ * Andrey Volk <andywolk@gmail.com>
  *
  *
  * switch_module_interfaces.h -- Module Interface Definitions
@@ -614,6 +615,38 @@ struct switch_directory_handle {
 	void *private_info;
 };
 
+/*! \brief Abstract interface to a database module */
+struct switch_database_interface {
+	/*! the name of the interface */
+	const char *interface_name;
+	switch_status_t(*handle_new)(char *dsn, switch_database_interface_handle_t **dih);
+	switch_status_t(*handle_destroy)(switch_database_interface_handle_t **dih); 
+	switch_status_t(*flush)(switch_database_interface_handle_t *dih);
+	switch_status_t(*exec_detailed)(const char *file, const char *func, int line, 
+		switch_database_interface_handle_t *dih, const char *sql, char **err);
+	switch_status_t(*exec_string)(switch_database_interface_handle_t *dih, const char *sql, char *resbuf, size_t len, char **err);
+	switch_status_t(*sql_set_auto_commit_attr)(switch_database_interface_handle_t *dih, switch_bool_t on);
+	switch_status_t(*commit)(switch_database_interface_handle_t *dih);
+	switch_status_t(*rollback)(switch_database_interface_handle_t *dih);
+	switch_status_t(*callback_exec_detailed)(const char *file, const char *func, int line,
+		switch_database_interface_handle_t *dih, const char *sql, switch_core_db_callback_func_t callback, void *pdata, char **err);
+	switch_status_t(*affected_rows)(switch_database_interface_handle_t *dih, int *affected_rows);
+
+	/*! list of supported dsn prefixes */
+	char **prefixes;
+	switch_thread_rwlock_t *rwlock;
+	int refs;
+	switch_mutex_t *reflock;
+	switch_loadable_module_interface_t *parent;
+	struct switch_database_interface *next;
+};
+
+/*! an abstract representation of a database interface. */
+struct switch_database_interface_handle {
+	switch_cache_db_database_interface_options_t connection_options;
+	void *handle;
+};
+
 struct switch_audio_codec_settings {
 	int unused;
 };
diff --git a/src/include/switch_types.h b/src/include/switch_types.h
index 53c25c395c..de2effd336 100644
--- a/src/include/switch_types.h
+++ b/src/include/switch_types.h
@@ -28,6 +28,7 @@
  * Joseph Sullivan <jossulli@amazon.com>
  * Raymond Chandler <intralanman@freeswitch.org>
  * Emmanuel Schmidbauer <e.schmidbauer@gmail.com>
+ * Andrey Volk <andywolk@gmail.com>
  *
  * switch_types.h -- Data Types
  *
@@ -400,6 +401,7 @@ typedef enum {
 	SWITCH_LIMIT_INTERFACE,
 	SWITCH_CHAT_APPLICATION_INTERFACE,
 	SWITCH_JSON_API_INTERFACE,
+	SWITCH_DATABASE_INTERFACE,
 } switch_module_interface_name_t;
 
 typedef enum {
@@ -2290,6 +2292,7 @@ typedef struct switch_codec_fmtp switch_codec_fmtp_t;
 typedef struct switch_odbc_handle switch_odbc_handle_t;
 typedef struct switch_pgsql_handle switch_pgsql_handle_t;
 typedef struct switch_pgsql_result switch_pgsql_result_t;
+typedef struct switch_database_interface_handle switch_database_interface_handle_t;
 
 typedef struct switch_io_routines switch_io_routines_t;
 typedef struct switch_speech_handle switch_speech_handle_t;
@@ -2313,6 +2316,7 @@ typedef struct switch_management_interface switch_management_interface_t;
 typedef struct switch_core_port_allocator switch_core_port_allocator_t;
 typedef struct switch_media_bug switch_media_bug_t;
 typedef struct switch_limit_interface switch_limit_interface_t;
+typedef struct switch_database_interface switch_database_interface_t;
 
 typedef void (*hashtable_destructor_t)(void *ptr);
 
diff --git a/src/switch_core.c b/src/switch_core.c
index b4128cc19a..d81b7e520c 100644
--- a/src/switch_core.c
+++ b/src/switch_core.c
@@ -29,6 +29,7 @@
  * Marcel Barbulescu <marcelbarbulescu@gmail.com>
  * Joseph Sullivan <jossulli@amazon.com>
  * Seven Du <dujinfang@gmail.com>
+ * Andrey Volk <andywolk@gmail.com>
  *
  * switch_core.c -- Main Core Library
  *
@@ -2015,10 +2016,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switc
 
 	switch_core_state_machine_init(runtime.memory_pool);
 
-	if (switch_core_sqldb_start(runtime.memory_pool, switch_test_flag((&runtime), SCF_USE_SQL) ? SWITCH_TRUE : SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
-		*err = "Error activating database";
-		return SWITCH_STATUS_FALSE;
-	}
 	switch_core_media_init();
 	switch_scheduler_task_thread_start();
 
@@ -2343,11 +2340,7 @@ static void switch_load_core_config(const char *file)
 				} else if (!strcasecmp(var, "core-db-name") && !zstr(val)) {
 					runtime.dbname = switch_core_strdup(runtime.memory_pool, val);
 				} else if (!strcasecmp(var, "core-db-dsn") && !zstr(val)) {
-					if (switch_odbc_available() || switch_pgsql_available()) {
-						runtime.odbc_dsn = switch_core_strdup(runtime.memory_pool, val);
-					} else {
-						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ODBC AND PGSQL ARE NOT AVAILABLE!\n");
-					}
+					runtime.odbc_dsn = switch_core_strdup(runtime.memory_pool, val);
 				} else if (!strcasecmp(var, "core-non-sqlite-db-required") && !zstr(val)) {
 					switch_set_flag((&runtime), SCF_CORE_NON_SQLITE_DB_REQ);
 				} else if (!strcasecmp(var, "core-dbtype") && !zstr(val)) {
@@ -2425,6 +2418,20 @@ SWITCH_DECLARE(const char *) switch_core_banner(void)
 			"\n");
 }
 
+switch_status_t switch_core_sqldb_init(const char **err)
+{
+	if (switch_core_check_core_db_dsn() != SWITCH_STATUS_SUCCESS) {
+		*err = "NO SUITABLE DATABASE INTERFACE IS AVAILABLE TO SERVE 'core-db-dsn'!\n";
+		return SWITCH_STATUS_GENERR;
+	}
+
+	if (switch_core_sqldb_start(runtime.memory_pool, switch_test_flag((&runtime), SCF_USE_SQL) ? SWITCH_TRUE : SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
+		*err = "Error activating database";
+		return SWITCH_STATUS_GENERR;
+	}
+
+	return SWITCH_STATUS_SUCCESS;
+}
 
 SWITCH_DECLARE(switch_status_t) switch_core_init_and_modload(switch_core_flag_t flags, switch_bool_t console, const char **err)
 {
@@ -2969,6 +2976,13 @@ SWITCH_DECLARE(switch_bool_t) switch_core_ready_outbound(void)
 	return (switch_test_flag((&runtime), SCF_SHUTTING_DOWN) || switch_test_flag((&runtime), SCF_NO_NEW_OUTBOUND_SESSIONS)) ? SWITCH_FALSE : SWITCH_TRUE;
 }
 
+void switch_core_sqldb_destroy()
+{
+	if (switch_test_flag((&runtime), SCF_USE_SQL)) {
+		switch_core_sqldb_stop();
+	}
+}
+
 SWITCH_DECLARE(switch_status_t) switch_core_destroy(void)
 {
 	switch_event_t *event;
@@ -2989,9 +3003,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_destroy(void)
 
 	switch_ssl_destroy_ssl_locks();
 
-	if (switch_test_flag((&runtime), SCF_USE_SQL)) {
-		switch_core_sqldb_stop();
-	}
 	switch_scheduler_task_thread_stop();
 
 	switch_rtp_shutdown();
diff --git a/src/switch_core_sqldb.c b/src/switch_core_sqldb.c
index feb69d6b8e..9c6dc1ab55 100644
--- a/src/switch_core_sqldb.c
+++ b/src/switch_core_sqldb.c
@@ -27,6 +27,7 @@
  * Michael Jerris <mike@jerris.com>
  * Paul D. Tinsley <pdt at jackhammer.org>
  * Emmanuel Schmidbauer <eschmidbauer@gmail.com>
+ * Andrey Volk <andywolk@gmail.com>
  *
  *
  * switch_core_sqldb.c -- Main Core Library (statistics tracker)
@@ -77,6 +78,9 @@ static struct {
 static void switch_core_sqldb_start_thread(void);
 static void switch_core_sqldb_stop_thread(void);
 
+#define database_interface_handle_callback_exec(database_interface, dih, sql, callback, pdata, err) database_interface->callback_exec_detailed(__FILE__, (char *)__SWITCH_FUNC__, __LINE__, dih, sql, callback, pdata, err)
+#define database_interface_handle_exec(database_interface, dih, sql, err) database_interface->exec_detailed(__FILE__, (char *)__SWITCH_FUNC__, __LINE__, dih, sql, err)
+
 static switch_cache_db_handle_t *create_handle(switch_cache_db_handle_type_t type)
 {
 	switch_cache_db_handle_t *new_dbh = NULL;
@@ -135,6 +139,35 @@ static void del_handle(switch_cache_db_handle_t *dbh)
 	switch_mutex_unlock(sql_manager.dbh_mutex);
 }
 
+SWITCH_DECLARE(void) switch_cache_db_database_interface_flush_handles(switch_database_interface_t *database_interface)
+{
+	switch_cache_db_handle_t *dbh_ptr = NULL;
+
+	switch_mutex_lock(sql_manager.dbh_mutex);
+
+	for (dbh_ptr = sql_manager.handle_pool; dbh_ptr; dbh_ptr = dbh_ptr->next) {
+		if (switch_mutex_trylock(dbh_ptr->mutex) == SWITCH_STATUS_SUCCESS) {
+			if (dbh_ptr->type != SCDB_TYPE_DATABASE_INTERFACE) {
+				continue;
+			}
+
+			if (dbh_ptr->native_handle.database_interface_dbh->connection_options.database_interface != database_interface) {
+				continue;
+			}
+
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "Dropping DB connection %s\n", dbh_ptr->name);
+
+			database_interface->handle_destroy(&dbh_ptr->native_handle.database_interface_dbh);
+
+			del_handle(dbh_ptr);
+			switch_mutex_unlock(dbh_ptr->mutex);
+			switch_core_destroy_memory_pool(&dbh_ptr->pool);
+		}
+	}
+
+	switch_mutex_unlock(sql_manager.dbh_mutex);
+}
+
 static switch_cache_db_handle_t *get_handle(const char *db_str, const char *user_str, const char *thread_str)
 {
 	switch_ssize_t hlen = -1;
@@ -155,7 +188,7 @@ static switch_cache_db_handle_t *get_handle(const char *db_str, const char *user
 
 	if (!r) {
 		for (dbh_ptr = sql_manager.handle_pool; dbh_ptr; dbh_ptr = dbh_ptr->next) {
-			if (dbh_ptr->hash == hash && (dbh_ptr->type != SCDB_TYPE_PGSQL || !dbh_ptr->use_count) && !switch_test_flag(dbh_ptr, CDF_PRUNE) &&
+			if (dbh_ptr->hash == hash && ((dbh_ptr->type != SCDB_TYPE_PGSQL && dbh_ptr->type != SCDB_TYPE_DATABASE_INTERFACE) || !dbh_ptr->use_count) && !switch_test_flag(dbh_ptr, CDF_PRUNE) &&
 				switch_mutex_trylock(dbh_ptr->mutex) == SWITCH_STATUS_SUCCESS) {
 				r = dbh_ptr;
 				break;
@@ -198,7 +231,7 @@ SWITCH_DECLARE(switch_status_t) _switch_core_db_handle(switch_cache_db_handle_t
 		dsn = "core";
 	}
 
-	if ((r = _switch_cache_db_get_db_handle_dsn(dbh, dsn, file, func, line)) != SWITCH_STATUS_SUCCESS) {
+	if ((r = _switch_cache_db_get_db_handle_dsn_ex(dbh, dsn, SWITCH_TRUE, file, func, line)) != SWITCH_STATUS_SUCCESS) {
 		*dbh = NULL;
 	}
 
@@ -234,6 +267,12 @@ static void sql_close(time_t prune)
 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "Dropping idle DB connection %s\n", dbh->name);
 
 			switch (dbh->type) {
+				case SCDB_TYPE_DATABASE_INTERFACE:
+				{
+					switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+					database_interface->handle_destroy(&dbh->native_handle.database_interface_dbh);
+				}
+				break;
 			case SCDB_TYPE_PGSQL:
 				{
 					switch_pgsql_handle_destroy(&dbh->native_handle.pgsql_dbh);
@@ -299,6 +338,12 @@ SWITCH_DECLARE(void) switch_cache_db_release_db_handle(switch_cache_db_handle_t
 	if (dbh && *dbh) {
 
 		switch((*dbh)->type) {
+		case SCDB_TYPE_DATABASE_INTERFACE:
+			{
+				switch_database_interface_t *database_interface = (*dbh)->native_handle.database_interface_dbh->connection_options.database_interface;
+				database_interface->flush((*dbh)->native_handle.database_interface_dbh);
+			}
+		break;
 		case SCDB_TYPE_PGSQL:
 			{
 				switch_pgsql_flush((*dbh)->native_handle.pgsql_dbh);
@@ -331,46 +376,115 @@ SWITCH_DECLARE(void) switch_cache_db_dismiss_db_handle(switch_cache_db_handle_t
 	switch_cache_db_release_db_handle(dbh);
 }
 
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+SWITCH_DECLARE(switch_status_t) switch_core_check_core_db_dsn()
+{
+	switch_status_t status = SWITCH_STATUS_FALSE;
+	switch_database_interface_t *database_interface;
+
+	if (!runtime.odbc_dsn) {
+		status = SWITCH_STATUS_SUCCESS;
+	} else {
+		char *colon_slashes = NULL;
+		if (NULL != (colon_slashes = strstr(runtime.odbc_dsn, "://")))
+		{
+			char prefix[16] = "";
+			strncpy(prefix, runtime.odbc_dsn, MIN(colon_slashes - runtime.odbc_dsn, 15));
+
+			if (!strncasecmp(prefix, "odbc", 4)) {
+				if (switch_odbc_available()) status = SWITCH_STATUS_SUCCESS;
+			}
+			else if (!strncasecmp(prefix, "sqlite", 6)) {
+				status = SWITCH_STATUS_SUCCESS;
+			}
+			else if (!strncasecmp(prefix, "pgsql", 5)) {
+				if (switch_pgsql_available()) status = SWITCH_STATUS_SUCCESS;
+			}
+			else if ((database_interface = switch_loadable_module_get_database_interface(prefix, NULL))) {
+				status = SWITCH_STATUS_SUCCESS;
+				UNPROTECT_INTERFACE(database_interface);
+			}
+		}
+		else if (strchr(runtime.odbc_dsn + 2, ':')) {
+			status = SWITCH_STATUS_SUCCESS;
+		}
+	}
+
+	return status;
+}
 
 SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle_dsn(switch_cache_db_handle_t **dbh, const char *dsn,
+	const char *file, const char *func, int line)
+{
+	return _switch_cache_db_get_db_handle_dsn_ex(dbh, dsn, SWITCH_FALSE, file, func, line);
+}
+
+SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle_dsn_ex(switch_cache_db_handle_t **dbh, const char *dsn, switch_bool_t make_module_no_unloadable,
 																   const char *file, const char *func, int line)
 {
 	switch_cache_db_connection_options_t connection_options = { {0} };
 	switch_cache_db_handle_type_t type;
+	switch_database_interface_t *database_interface = NULL;
 	char tmp[256] = "";
 	char *p;
 	switch_status_t status = SWITCH_STATUS_FALSE;
 	int i;
 
-	if (!strncasecmp(dsn, "pgsql://", 8)) {
-		type = SCDB_TYPE_PGSQL;
-		connection_options.pgsql_options.dsn = (char *)(dsn + 8);
-	} else if (!strncasecmp(dsn, "sqlite://", 9)) {
-		type = SCDB_TYPE_CORE_DB;
-		connection_options.core_db_options.db_path = (char *)(dsn + 9);
-	} else if ((!(i = strncasecmp(dsn, "odbc://", 7))) || strchr(dsn+2, ':')) {
-		type = SCDB_TYPE_ODBC;
+	char *colon_slashes = NULL;
+	if ( NULL != (colon_slashes = strstr(dsn, "://")) )
+	{
+		char prefix[16] = "";
+		strncpy(prefix, dsn, MIN(colon_slashes - dsn, 15));
 
-		if (i) {
-			switch_set_string(tmp, dsn);
-		} else {
-			switch_set_string(tmp, dsn+7);
+		if ((database_interface = switch_loadable_module_get_database_interface(prefix, NULL))) {
+			type = SCDB_TYPE_DATABASE_INTERFACE;
+			connection_options.database_interface_options.make_module_no_unloadable = make_module_no_unloadable;
+			connection_options.database_interface_options.database_interface = database_interface;
+			connection_options.database_interface_options.dsn = colon_slashes + 3;
+			strcpy(connection_options.database_interface_options.prefix, prefix);
+			UNPROTECT_INTERFACE(database_interface);
 		}
+	}
 
-		connection_options.odbc_options.dsn = tmp;
+	if (!connection_options.database_interface_options.dsn)
+	{		
+		if (!strncasecmp(dsn, "pgsql://", 8)) {
+			type = SCDB_TYPE_PGSQL;
+			connection_options.pgsql_options.dsn = (char *)(dsn + 8);
+		}
+		else if (!strncasecmp(dsn, "sqlite://", 9)) {
+			type = SCDB_TYPE_CORE_DB;
+			connection_options.core_db_options.db_path = (char *)(dsn + 9);
+		}
+		else if ((!(i = strncasecmp(dsn, "odbc://", 7))) || (strchr(dsn + 2, ':') && !colon_slashes)) {
+			type = SCDB_TYPE_ODBC;
 
-		if ((p = strchr(tmp, ':'))) {
-			*p++ = '\0';
-			connection_options.odbc_options.user = p;
+			if (i) {
+				switch_set_string(tmp, dsn);
+			}
+			else {
+				switch_set_string(tmp, dsn + 7);
+			}
 
-			if ((p = strchr(connection_options.odbc_options.user, ':'))) {
+			connection_options.odbc_options.dsn = tmp;
+
+			if ((p = strchr(tmp, ':'))) {
 				*p++ = '\0';
-				connection_options.odbc_options.pass = p;
+				connection_options.odbc_options.user = p;
+
+				if ((p = strchr(connection_options.odbc_options.user, ':'))) {
+					*p++ = '\0';
+					connection_options.odbc_options.pass = p;
+				}
 			}
 		}
-	} else {
-		type = SCDB_TYPE_CORE_DB;
-		connection_options.core_db_options.db_path = (char *)dsn;
+		else {
+			type = SCDB_TYPE_CORE_DB;
+			connection_options.core_db_options.db_path = (char *)dsn;
+		}
 	}
 
 	status = _switch_cache_db_get_db_handle(dbh, type, &connection_options, file, func, line);
@@ -416,6 +530,14 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h
 	}
 
 	switch (type) {
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			db_name = connection_options->database_interface_options.dsn;
+			odbc_user = NULL;
+			odbc_pass = NULL;
+			db_type = "database_interface";
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			db_name = connection_options->pgsql_options.dsn;
@@ -423,6 +545,7 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h
 			odbc_pass = NULL;
 			db_type = "pgsql";
 		}
+		break;
 	case SCDB_TYPE_ODBC:
 		{
 			db_name = connection_options->odbc_options.dsn;
@@ -454,14 +577,41 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h
 	snprintf(thread_str, sizeof(thread_str) - 1, "thread=\"%lu\"",  (unsigned long) (intptr_t) self);
 
 	if ((new_dbh = get_handle(db_str, db_callsite_str, thread_str))) {
-		switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG10,
-						  "Reuse Unused Cached DB handle %s [%s]\n", new_dbh->name, switch_cache_db_type_name(new_dbh->type));
+		if (type == SCDB_TYPE_DATABASE_INTERFACE) {
+			switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG10,
+				"Reuse Unused Cached DB handle %s [Database interface prefix: %s]\n", new_dbh->name, connection_options->database_interface_options.prefix);
+		} else {
+			switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG10,
+				"Reuse Unused Cached DB handle %s [%s]\n", new_dbh->name, switch_cache_db_type_name(new_dbh->type));
+		}
 	} else {
 		switch_core_db_t *db = NULL;
 		switch_odbc_handle_t *odbc_dbh = NULL;
 		switch_pgsql_handle_t *pgsql_dbh = NULL;
+		switch_database_interface_handle_t *database_interface_dbh = NULL;		
 
 		switch (type) {
+		case SCDB_TYPE_DATABASE_INTERFACE:
+			{
+				switch_database_interface_t *database_interface = connection_options->database_interface_options.database_interface;
+
+				if (SWITCH_STATUS_SUCCESS != database_interface->handle_new(connection_options->database_interface_options.dsn, &database_interface_dbh)) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure! Can't create new handle! Can't connect to DSN %s\n", connection_options->database_interface_options.dsn);
+					goto end;
+				}
+
+				if (database_interface_dbh) {
+					database_interface_dbh->connection_options = connection_options->database_interface_options;
+				
+					if (connection_options->database_interface_options.make_module_no_unloadable == SWITCH_TRUE)
+					{
+						PROTECT_INTERFACE(database_interface)
+							switch_loadable_module_protect(database_interface->parent->module_name);
+						UNPROTECT_INTERFACE(database_interface)
+					}
+				}
+			}
+			break;
 		case SCDB_TYPE_PGSQL:
 			{
 				if (!switch_pgsql_available()) {
@@ -478,7 +628,6 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h
 			break;
 		case SCDB_TYPE_ODBC:
 			{
-
 				if (!switch_odbc_available()) {
 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure! ODBC NOT AVAILABLE! Can't connect to DSN %s\n", connection_options->odbc_options.dsn);
 					goto end;
@@ -490,8 +639,6 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h
 						switch_odbc_handle_destroy(&odbc_dbh);
 					}
 				}
-
-
 			}
 			break;
 		case SCDB_TYPE_CORE_DB:
@@ -504,7 +651,7 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h
 			goto end;
 		}
 
-		if (!db && !odbc_dbh && !pgsql_dbh) {
+		if (!db && !odbc_dbh && !pgsql_dbh && !database_interface_dbh) {
 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure to connect to %s %s!\n", switch_cache_db_type_name(type), db_name);
 			goto end;
 		}
@@ -514,7 +661,9 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h
 		switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG10,
 						  "Create Cached DB handle %s [%s] %s:%d\n", new_dbh->name, switch_cache_db_type_name(type), file, line);
 
-		if (db) {
+		if (database_interface_dbh) {
+			new_dbh->native_handle.database_interface_dbh = database_interface_dbh;
+		} else if (db) {
 			new_dbh->native_handle.core_db_dbh = db;
 		} else if (odbc_dbh) {
 			new_dbh->native_handle.odbc_dbh = odbc_dbh;
@@ -552,6 +701,13 @@ static switch_status_t switch_cache_db_execute_sql_real(switch_cache_db_handle_t
 	}
 
 	switch (dbh->type) {
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+			type = (char *)dbh->native_handle.database_interface_dbh->connection_options.prefix;
+			status = database_interface_handle_exec(database_interface, dbh->native_handle.database_interface_dbh, sql, &errmsg);
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			type = "PGSQL";
@@ -698,6 +854,13 @@ SWITCH_DECLARE(int) switch_cache_db_affected_rows(switch_cache_db_handle_t *dbh)
 			return switch_odbc_handle_affected_rows(dbh->native_handle.odbc_dbh);
 		}
 		break;
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+			int affected_rows = 0;
+			database_interface->affected_rows(dbh->native_handle.database_interface_dbh, &affected_rows);
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			return switch_pgsql_handle_affected_rows(dbh->native_handle.pgsql_dbh);
@@ -721,6 +884,11 @@ SWITCH_DECLARE(int) switch_cache_db_load_extension(switch_cache_db_handle_t *dbh
 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "load extension not supported by type ODBC!\n");
 		}
 		break;
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "load extension not supported by type DATABASE_INTERFACE!\n");
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "load extension not supported by type PGSQL!\n");
@@ -783,6 +951,12 @@ SWITCH_DECLARE(char *) switch_cache_db_execute_sql2str(switch_cache_db_handle_t
 			status = switch_odbc_handle_exec_string(dbh->native_handle.odbc_dbh, sql, str, len, err);
 		}
 		break;
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+			status = database_interface->exec_string(dbh->native_handle.database_interface_dbh, sql, str, len, err);
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			status = switch_pgsql_handle_exec_string(dbh->native_handle.pgsql_dbh, sql, str, len, err);
@@ -887,6 +1061,18 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_persistant_execute_trans_full(sw
 				}
 			}
 			break;
+		case SCDB_TYPE_DATABASE_INTERFACE:
+			{
+				switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+				switch_status_t result;
+
+				if ((result = database_interface->sql_set_auto_commit_attr(dbh->native_handle.database_interface_dbh, 0)) != SWITCH_STATUS_SUCCESS) {
+					char tmp[100];
+					switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result);
+					errmsg = strdup(tmp);
+				}					
+			}
+			break;
 		case SCDB_TYPE_PGSQL:
 			{
 				switch_pgsql_status_t result;
@@ -922,6 +1108,17 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_persistant_execute_trans_full(sw
 						switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 1);
 					}
 					break;
+				case SCDB_TYPE_DATABASE_INTERFACE:
+					{
+						switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+						switch_status_t result;
+
+						if ((result = database_interface->commit(dbh->native_handle.database_interface_dbh)) != SWITCH_STATUS_SUCCESS) {
+							char tmp[100];
+							switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to commit transaction", result);
+						}
+					}
+					break;
 				case SCDB_TYPE_PGSQL:
 					{
 						switch_pgsql_SQLEndTran(dbh->native_handle.pgsql_dbh, 1);
@@ -997,6 +1194,17 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_persistant_execute_trans_full(sw
 			switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 1);
 		}
 		break;
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+			switch_status_t result;
+	
+			if ((result = database_interface->commit(dbh->native_handle.database_interface_dbh)) != SWITCH_STATUS_SUCCESS) {
+				char tmp[100];
+				switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to commit transaction", result);
+			}
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			switch_pgsql_SQLEndTran(dbh->native_handle.pgsql_dbh, 1);
@@ -1058,6 +1266,17 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_execute_sql_event_callback(switc
 	h.pdata = pdata;
 
 	switch (dbh->type) {
+		case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+			switch_status_t result;
+
+			if ((result = database_interface_handle_callback_exec(database_interface, dbh->native_handle.database_interface_dbh, sql, helper_callback, &h, err)) != SWITCH_STATUS_SUCCESS) {
+				char tmp[100];
+				switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to execute_sql_event_callback", result);
+			}
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			status = switch_pgsql_handle_callback_exec(dbh->native_handle.pgsql_dbh, sql, helper_callback, &h, err);
@@ -1113,6 +1332,21 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_execute_sql_event_callback_err(s
 	h.pdata = pdata;
 
 	switch (dbh->type) {
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+			switch_status_t result;
+
+			if ((result = database_interface_handle_callback_exec(database_interface, dbh->native_handle.database_interface_dbh, sql, helper_callback, &h, err)) != SWITCH_STATUS_SUCCESS) {
+				char tmp[100];
+				switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to execute_sql_event_callback_err", result);
+			} else {
+				if (err && *err) {
+					(*err_callback)(pdata, (const char*)*err);
+				}
+			}
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			status = switch_pgsql_handle_callback_exec(dbh->native_handle.pgsql_dbh, sql, helper_callback, &h, err);
@@ -1173,6 +1407,17 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_execute_sql_callback(switch_cach
 
 
 	switch (dbh->type) {
+		case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+			switch_status_t result;
+
+			if ((result = database_interface_handle_callback_exec(database_interface, dbh->native_handle.database_interface_dbh, sql, callback, pdata, err)) != SWITCH_STATUS_SUCCESS) {
+				char tmp[100];
+				switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to execute_sql_callback", result);
+			}
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			status = switch_pgsql_handle_callback_exec(dbh->native_handle.pgsql_dbh, sql, callback, pdata, err);
@@ -1223,6 +1468,21 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_execute_sql_callback_err(switch_
 
 
 	switch (dbh->type) {
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+			switch_status_t result;
+
+			if ((result = database_interface_handle_callback_exec(database_interface, dbh->native_handle.database_interface_dbh, sql, callback, pdata, err)) != SWITCH_STATUS_SUCCESS) {
+				char tmp[100];
+				switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to execute_sql_callback_err", result);
+			} else {
+				if (err && *err) {
+					(*err_callback)(pdata, (const char*)*err);
+				}
+			}
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			status = switch_pgsql_handle_callback_exec(dbh->native_handle.pgsql_dbh, sql, callback, pdata, err);
@@ -1318,6 +1578,31 @@ SWITCH_DECLARE(switch_bool_t) switch_cache_db_test_reactive(switch_cache_db_hand
 	if (io_mutex) switch_mutex_lock(io_mutex);
 
 	switch (dbh->type) {
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_database_interface_t *database_interface = dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+			switch_status_t result;
+
+			if ((result = database_interface_handle_exec(database_interface, dbh->native_handle.database_interface_dbh, test_sql, NULL)) != SWITCH_STATUS_SUCCESS) {
+				char tmp[100];
+				switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to test_reactive with test_sql", result);
+
+				if (drop_sql) {
+					if ((result = database_interface_handle_exec(database_interface, dbh->native_handle.database_interface_dbh, drop_sql, NULL)) != SWITCH_STATUS_SUCCESS) {
+						char tmp[100];
+						switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to test_reactive with drop_sql", result);
+					}
+
+					if ((result = database_interface_handle_exec(database_interface, dbh->native_handle.database_interface_dbh, reactive_sql, NULL)) != SWITCH_STATUS_SUCCESS) {
+						char tmp[100];
+						switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to test_reactive with reactive_sql", result);
+					}
+
+					r = result;
+				}
+			}
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			if (switch_pgsql_handle_exec(dbh->native_handle.pgsql_dbh, test_sql, NULL) != SWITCH_PGSQL_SUCCESS) {
@@ -1940,6 +2225,18 @@ static uint32_t do_trans(switch_sql_queue_manager_t *qm)
 			}
 		}
 		break;
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_database_interface_t *database_interface = qm->event_db->native_handle.database_interface_dbh->connection_options.database_interface;
+			switch_status_t result;
+
+			if ((result = database_interface->sql_set_auto_commit_attr(qm->event_db->native_handle.database_interface_dbh, 0)) != SWITCH_STATUS_SUCCESS) {
+				char tmp[100];
+				switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result);
+				errmsg = strdup(tmp);
+			}
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			switch_pgsql_status_t result;
@@ -2016,6 +2313,17 @@ static uint32_t do_trans(switch_sql_queue_manager_t *qm)
 			switch_odbc_SQLSetAutoCommitAttr(qm->event_db->native_handle.odbc_dbh, 1);
 		}
 		break;
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		{
+			switch_database_interface_t *database_interface = qm->event_db->native_handle.database_interface_dbh->connection_options.database_interface;
+			switch_status_t result;
+
+			if ((result = database_interface->commit(qm->event_db->native_handle.database_interface_dbh)) != SWITCH_STATUS_SUCCESS) {
+				char tmp[100];
+				switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to commit transaction", result);
+			}
+		}
+		break;
 	case SCDB_TYPE_PGSQL:
 		{
 			switch_pgsql_SQLEndTran(qm->event_db->native_handle.pgsql_dbh, 1);
@@ -2072,6 +2380,8 @@ static void *SWITCH_THREAD_FUNC switch_user_sql_thread(switch_thread_t *thread,
 	switch_mutex_lock(qm->cond_mutex);
 
 	switch (qm->event_db->type) {
+	case SCDB_TYPE_DATABASE_INTERFACE:
+		break;
 	case SCDB_TYPE_PGSQL:
 		break;
 	case SCDB_TYPE_ODBC:
@@ -3390,6 +3700,7 @@ switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_
 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening DB\n");
 
 	switch (sql_manager.dbh->type) {
+	case SCDB_TYPE_DATABASE_INTERFACE:
 	case SCDB_TYPE_PGSQL:
 	case SCDB_TYPE_ODBC:
 		if (switch_test_flag((&runtime), SCF_CLEAR_SQL)) {
@@ -3440,6 +3751,7 @@ switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_
 
 
 	switch (sql_manager.dbh->type) {
+	case SCDB_TYPE_DATABASE_INTERFACE:
 	case SCDB_TYPE_PGSQL:
 	case SCDB_TYPE_ODBC:
 		{
@@ -3481,6 +3793,18 @@ switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_
 					}
 				}
 				break;
+			case SCDB_TYPE_DATABASE_INTERFACE:
+				{
+					switch_database_interface_t *database_interface = sql_manager.dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+					switch_status_t result;
+
+					if ((result = database_interface->sql_set_auto_commit_attr(sql_manager.dbh->native_handle.database_interface_dbh, 0)) != SWITCH_STATUS_SUCCESS) {
+						char tmp[100];
+						switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result);
+						err = strdup(tmp);
+					}
+				}
+				break;
 			case SCDB_TYPE_PGSQL:
 				{
 					switch_pgsql_status_t result;
@@ -3514,6 +3838,18 @@ switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_
 						}
 					}
 					break;
+				case SCDB_TYPE_DATABASE_INTERFACE:
+					{
+						switch_database_interface_t *database_interface = sql_manager.dbh->native_handle.database_interface_dbh->connection_options.database_interface;
+						switch_status_t result;
+
+						if ((result = database_interface->commit(sql_manager.dbh->native_handle.database_interface_dbh)) != SWITCH_STATUS_SUCCESS) {
+							char tmp[100];
+							switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to commit transaction", result);
+							err = strdup(tmp);
+						}
+					}
+					break;
 				case SCDB_TYPE_PGSQL:
 					{
 						if (switch_pgsql_SQLEndTran(sql_manager.dbh->native_handle.pgsql_dbh, 1) != SWITCH_PGSQL_SUCCESS ||
diff --git a/src/switch_loadable_module.c b/src/switch_loadable_module.c
index 38de325d2c..dfd8f310a9 100644
--- a/src/switch_loadable_module.c
+++ b/src/switch_loadable_module.c
@@ -25,12 +25,14 @@
  *
  * Anthony Minessale II <anthm@freeswitch.org>
  * Seven Du <dujinfang@gmail.com>
+ * Andrey Volk <andywolk@gmail.com>
  *
  * switch_loadable_module.c -- Loadable Modules
  *
  */
 
 #include <switch.h>
+#include "private/switch_core_pvt.h"
 
 /* for apr_pstrcat */
 #include <apr_strings.h>
@@ -47,6 +49,12 @@ typedef struct switch_file_node_s {
 	struct switch_file_node_s *next;
 } switch_file_node_t;
 
+typedef struct switch_database_node_s {
+	const switch_database_interface_t *ptr;
+	const char *interface_name;
+	struct switch_database_node_s *next;
+} switch_database_node_t;
+
 typedef struct switch_codec_node_s {
 	const switch_codec_interface_t *ptr;
 	const char *interface_name;
@@ -67,6 +75,7 @@ struct switch_loadable_module {
 	switch_status_t status;
 	switch_thread_t *thread;
 	switch_bool_t shutting_down;
+	switch_loadable_module_type_t type;
 };
 
 struct switch_loadable_module_container {
@@ -87,6 +96,7 @@ struct switch_loadable_module_container {
 	switch_hash_t *say_hash;
 	switch_hash_t *management_hash;
 	switch_hash_t *limit_hash;
+	switch_hash_t *database_hash;
 	switch_hash_t *secondary_recover_hash;
 	switch_mutex_t *mutex;
 	switch_memory_pool_t *pool;
@@ -95,7 +105,7 @@ struct switch_loadable_module_container {
 static struct switch_loadable_module_container loadable_modules;
 static switch_status_t do_shutdown(switch_loadable_module_t *module, switch_bool_t shutdown, switch_bool_t unload, switch_bool_t fail_if_busy,
 								   const char **err);
-static switch_status_t switch_loadable_module_load_module_ex(const char *dir, const char *fname, switch_bool_t runtime, switch_bool_t global, const char **err);
+static switch_status_t switch_loadable_module_load_module_ex(const char *dir, const char *fname, switch_bool_t runtime, switch_bool_t global, const char **err, switch_loadable_module_type_t type, switch_hash_t *event_hash);
 
 static void *SWITCH_THREAD_FUNC switch_loadable_module_exec(switch_thread_t *thread, void *obj)
 {
@@ -143,11 +153,28 @@ static void switch_loadable_module_runtime(void)
 	switch_mutex_unlock(loadable_modules.mutex);
 }
 
-static switch_status_t switch_loadable_module_process(char *key, switch_loadable_module_t *new_module)
+static switch_status_t switch_loadable_module_process(char *key, switch_loadable_module_t *new_module, switch_hash_t *event_hash)
 {
 	switch_event_t *event;
+	int *event_num = NULL;
+	char str_event_num[10];
+	void *val;
 	int added = 0;
 
+	if (event_hash)	{
+		if ((val = switch_core_hash_find(event_hash, "0"))) {
+			event_num = (int*)val;
+		} else {
+			if (!(event_num = malloc(sizeof(int)))) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Allocation error.\n");
+				return SWITCH_STATUS_MEMERR;
+			}
+
+			*event_num = 0;
+			switch_core_hash_insert(event_hash, "0", (const void*)event_num);
+		}
+	}
+
 	new_module->key = switch_core_strdup(new_module->pool, key);
 
 	switch_mutex_lock(loadable_modules.mutex);
@@ -166,7 +193,14 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+					
+					if (!event_hash) {
+						switch_event_fire(&event);
+					} else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 			}
@@ -232,7 +266,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "module", new_module->module_interface->module_name);
-						switch_event_fire(&event);
+
+						if (!event_hash) {
+							switch_event_fire(&event);
+						}
+						else {
+							sprintf(str_event_num, "%i", ++*event_num);
+							switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+						}
+
 						added++;
 					}
 				}
@@ -253,7 +295,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+
+					if (!event_hash) {
+						switch_event_fire(&event);
+					}
+					else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 				switch_core_hash_insert(loadable_modules.dialplan_hash, ptr->interface_name, (const void *) ptr);
@@ -274,7 +324,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+
+					if (!event_hash) {
+						switch_event_fire(&event);
+					}
+					else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 				switch_core_hash_insert(loadable_modules.timer_hash, ptr->interface_name, (const void *) ptr);
@@ -297,7 +355,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+
+					if (!event_hash) {
+						switch_event_fire(&event);
+					}
+					else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 				switch_core_hash_insert(loadable_modules.application_hash, ptr->interface_name, (const void *) ptr);
@@ -320,7 +386,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+
+					if (!event_hash) {
+						switch_event_fire(&event);
+					}
+					else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 				switch_core_hash_insert(loadable_modules.chat_application_hash, ptr->interface_name, (const void *) ptr);
@@ -343,7 +417,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+
+					if (!event_hash) {
+						switch_event_fire(&event);
+					}
+					else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 				switch_core_hash_insert(loadable_modules.api_hash, ptr->interface_name, (const void *) ptr);
@@ -366,7 +448,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+
+					if (!event_hash) {
+						switch_event_fire(&event);
+					}
+					else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 				switch_core_hash_insert(loadable_modules.json_api_hash, ptr->interface_name, (const void *) ptr);
@@ -394,7 +484,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "module", new_module->module_interface->module_name);
-						switch_event_fire(&event);
+
+						if (!event_hash) {
+							switch_event_fire(&event);
+						}
+						else {
+							sprintf(str_event_num, "%i", ++*event_num);
+							switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+						}
+
 						added++;
 					}
 					node = switch_core_alloc(new_module->pool, sizeof(*node));
@@ -410,6 +508,52 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 		}
 	}
 
+	if (new_module->module_interface->database_interface) {
+		const switch_database_interface_t *ptr;
+
+		for (ptr = new_module->module_interface->database_interface; ptr; ptr = ptr->next) {
+			if (!ptr->interface_name) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load database interface from %s due to no interface name.\n", key);
+			}
+			else if (!ptr->prefixes) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load database interface from %s due to no prefixes.\n", key);
+			}
+			else {
+				int i;
+				switch_database_node_t *node, *head;
+
+				for (i = 0; ptr->prefixes[i]; i++) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding dsn prefix '%s'\n", ptr->prefixes[i]);
+					if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
+						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "database");
+						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->prefixes[i]);
+						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
+						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
+						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "module", new_module->module_interface->module_name);
+
+						if (!event_hash) {
+							switch_event_fire(&event);
+						}
+						else {
+							sprintf(str_event_num, "%i", ++*event_num);
+							switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+						}
+
+						added++;
+					}
+					node = switch_core_alloc(new_module->pool, sizeof(*node));
+					node->ptr = ptr;
+					node->interface_name = switch_core_strdup(new_module->pool, new_module->module_interface->module_name);
+					if ((head = switch_core_hash_find(loadable_modules.database_hash, ptr->prefixes[i]))) {
+						node->next = head;
+					}
+
+					switch_core_hash_insert(loadable_modules.database_hash, ptr->prefixes[i], (const void *)node);
+				}
+			}
+		}
+	}
+
 	if (new_module->module_interface->speech_interface) {
 		const switch_speech_interface_t *ptr;
 
@@ -423,7 +567,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+
+					if (!event_hash) {
+						switch_event_fire(&event);
+					}
+					else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 				switch_core_hash_insert(loadable_modules.speech_hash, ptr->interface_name, (const void *) ptr);
@@ -444,7 +596,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+
+					if (!event_hash) {
+						switch_event_fire(&event);
+					}
+					else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 				switch_core_hash_insert(loadable_modules.asr_hash, ptr->interface_name, (const void *) ptr);
@@ -465,7 +625,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+
+					if (!event_hash) {
+						switch_event_fire(&event);
+					}
+					else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 				switch_core_hash_insert(loadable_modules.directory_hash, ptr->interface_name, (const void *) ptr);
@@ -486,7 +654,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+
+					if (!event_hash) {
+						switch_event_fire(&event);
+					}
+					else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 				switch_core_hash_insert(loadable_modules.chat_hash, ptr->interface_name, (const void *) ptr);
@@ -507,7 +683,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-					switch_event_fire(&event);
+
+					if (!event_hash) {
+						switch_event_fire(&event);
+					}
+					else {
+						sprintf(str_event_num, "%i", ++*event_num);
+						switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+					}
+
 					added++;
 				}
 				switch_core_hash_insert(loadable_modules.say_hash, ptr->interface_name, (const void *) ptr);
@@ -534,7 +718,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->relative_oid);
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-						switch_event_fire(&event);
+
+						if (!event_hash) {
+							switch_event_fire(&event);
+						}
+						else {
+							sprintf(str_event_num, "%i", ++*event_num);
+							switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+						}
+
 						added++;
 					}
 				}
@@ -561,7 +753,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-						switch_event_fire(&event);
+
+						if (!event_hash) {
+							switch_event_fire(&event);
+						}
+						else {
+							sprintf(str_event_num, "%i", ++*event_num);
+							switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+						}
+
 						added++;
 					}
 				}
@@ -576,7 +776,15 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", new_module->key);
 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
-			switch_event_fire(&event);
+
+			if (!event_hash) {
+				switch_event_fire(&event);
+			}
+			else {
+				sprintf(str_event_num, "%i", ++*event_num);
+				switch_core_hash_insert(event_hash, (const char*)str_event_num, (const void*)event);
+			}
+
 			added++;
 		}
 	}
@@ -1215,6 +1423,62 @@ static switch_status_t switch_loadable_module_unprocess(switch_loadable_module_t
 		}
 	}
 
+	if (old_module->module_interface->database_interface) {
+		const switch_database_interface_t *ptr;
+		switch_database_node_t *node, *head, *last = NULL;
+
+		for (ptr = old_module->module_interface->database_interface; ptr; ptr = ptr->next) {
+			if (ptr->interface_name) {
+				int i;
+
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n",
+					ptr->interface_name);
+
+				if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) {
+					switch_thread_rwlock_unlock(ptr->rwlock);
+				}
+				else {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name);
+				}
+
+				for (i = 0; ptr->prefixes[i]; i++) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting dsn prefix '%s'\n", ptr->prefixes[i]);
+					if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
+						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "database");
+						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->prefixes[i]);
+						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "module", old_module->module_interface->module_name);
+						switch_event_fire(&event);
+						removed++;
+					}
+
+					if ((head = switch_core_hash_find(loadable_modules.database_hash, ptr->prefixes[i]))) {
+						for (node = head; node; node = node->next) {
+							if (!strcmp(node->interface_name, old_module->module_interface->module_name)) {
+								if (node == head) {
+									if ((node = node->next)) {
+										switch_core_hash_insert(loadable_modules.database_hash, ptr->prefixes[i], (const void *)node);
+									}
+									else {
+										switch_core_hash_delete(loadable_modules.database_hash, ptr->prefixes[i]);
+									}
+								}
+								else {
+									if (last) {
+										last->next = node->next;
+									}
+								}
+								break;
+							}
+							last = node;
+						}
+					}
+				}
+
+				switch_cache_db_database_interface_flush_handles(old_module->module_interface->database_interface);
+			}
+		}
+	}
+
 	if (old_module->module_interface->speech_interface) {
 		const switch_speech_interface_t *ptr;
 
@@ -1546,10 +1810,10 @@ static switch_status_t switch_loadable_module_load_file(char *path, char *filena
 }
 SWITCH_DECLARE(switch_status_t) switch_loadable_module_load_module(const char *dir, const char *fname, switch_bool_t runtime, const char **err)
 {
-	return switch_loadable_module_load_module_ex(dir, fname, runtime, SWITCH_FALSE, err);
+	return switch_loadable_module_load_module_ex(dir, fname, runtime, SWITCH_FALSE, err, SWITCH_LOADABLE_MODULE_TYPE_COMMON, NULL);
 }
 
-static switch_status_t switch_loadable_module_load_module_ex(const char *dir, const char *fname, switch_bool_t runtime, switch_bool_t global, const char **err)
+static switch_status_t switch_loadable_module_load_module_ex(const char *dir, const char *fname, switch_bool_t runtime, switch_bool_t global, const char **err, switch_loadable_module_type_t type, switch_hash_t *event_hash)
 {
 	switch_size_t len = 0;
 	char *path;
@@ -1593,7 +1857,9 @@ static switch_status_t switch_loadable_module_load_module_ex(const char *dir, co
 		*err = "Module already loaded";
 		status = SWITCH_STATUS_FALSE;
 	} else if ((status = switch_loadable_module_load_file(path, file, global, &new_module)) == SWITCH_STATUS_SUCCESS) {
-		if ((status = switch_loadable_module_process(file, new_module)) == SWITCH_STATUS_SUCCESS && runtime) {
+		new_module->type = type;
+
+		if ((status = switch_loadable_module_process(file, new_module, event_hash)) == SWITCH_STATUS_SUCCESS && runtime) {
 			if (new_module->switch_module_runtime) {
 				new_module->thread = switch_core_launch_thread(switch_loadable_module_exec, new_module, new_module->pool);
 			}
@@ -1628,6 +1894,30 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_exists(const char *mod)
 	return status;
 }
 
+SWITCH_DECLARE(switch_status_t) switch_loadable_module_protect(const char *mod)
+{
+	switch_loadable_module_t *module = NULL;
+	switch_status_t status = SWITCH_STATUS_FALSE;
+
+	if (zstr(mod)) {
+		return SWITCH_STATUS_FALSE;
+	}
+
+	switch_mutex_lock(loadable_modules.mutex);
+	if ((module = switch_core_hash_find(loadable_modules.module_hash, mod))) {
+		if (!module->perm) {
+			module->perm++;
+		}
+		status = SWITCH_STATUS_SUCCESS;
+	}
+	else {
+		status = SWITCH_STATUS_FALSE;
+	}
+	switch_mutex_unlock(loadable_modules.mutex);
+
+	return status;
+}
+
 SWITCH_DECLARE(switch_status_t) switch_loadable_module_unload_module(const char *dir, const char *fname, switch_bool_t force, const char **err)
 {
 	switch_loadable_module_t *module = NULL;
@@ -1801,7 +2091,7 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_build_dynamic(char *filen
 		module->thread = switch_core_launch_thread(switch_loadable_module_exec, module, module->pool);
 	}
 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Successfully Loaded [%s]\n", module_interface->module_name);
-	return switch_loadable_module_process((char *) module->filename, module);
+	return switch_loadable_module_process((char *) module->filename, module, NULL);
 }
 
 #ifdef WIN32
@@ -1826,12 +2116,17 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_init(switch_bool_t autolo
 	apr_finfo_t finfo = { 0 };
 	apr_dir_t *module_dir_handle = NULL;
 	apr_int32_t finfo_flags = APR_FINFO_DIRENT | APR_FINFO_TYPE | APR_FINFO_NAME;
+	char *precf = "pre_load_modules.conf";
 	char *cf = "modules.conf";
 	char *pcf = "post_load_modules.conf";
 	switch_xml_t cfg, xml;
 	unsigned char all = 0;
 	unsigned int count = 0;
 	const char *err;
+	switch_hash_t *event_hash;
+	switch_hash_index_t *hi;
+	void *hash_val;
+	switch_event_t *event;
 
 
 #ifdef WIN32
@@ -1869,21 +2164,94 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_init(switch_bool_t autolo
 	switch_core_hash_init_nocase(&loadable_modules.say_hash);
 	switch_core_hash_init_nocase(&loadable_modules.management_hash);
 	switch_core_hash_init_nocase(&loadable_modules.limit_hash);
+	switch_core_hash_init_nocase(&loadable_modules.database_hash);
 	switch_core_hash_init_nocase(&loadable_modules.dialplan_hash);
 	switch_core_hash_init(&loadable_modules.secondary_recover_hash);
 	switch_mutex_init(&loadable_modules.mutex, SWITCH_MUTEX_NESTED, loadable_modules.pool);
 
 	if (!autoload) return SWITCH_STATUS_SUCCESS;
+	
+	/*
+		switch_core_sqldb_init() is not yet ready and is executed after starting modules from pre_load_modules.conf
+		Modules loading procedure generates events used by sqldb.
+		This is why we should hold those events (storing in the event_hash) not firing them until sqldb is ready.
+	*/
+	switch_core_hash_init(&event_hash);
 
-	switch_loadable_module_load_module("", "CORE_SOFTTIMER_MODULE", SWITCH_FALSE, &err);
-	switch_loadable_module_load_module("", "CORE_PCM_MODULE", SWITCH_FALSE, &err);
-	switch_loadable_module_load_module("", "CORE_SPEEX_MODULE", SWITCH_FALSE, &err);
+	switch_loadable_module_load_module_ex("", "CORE_SOFTTIMER_MODULE", SWITCH_FALSE, SWITCH_FALSE, &err, SWITCH_LOADABLE_MODULE_TYPE_COMMON, event_hash);
+	switch_loadable_module_load_module_ex("", "CORE_PCM_MODULE", SWITCH_FALSE, SWITCH_FALSE, &err, SWITCH_LOADABLE_MODULE_TYPE_COMMON, event_hash);
+	switch_loadable_module_load_module_ex("", "CORE_SPEEX_MODULE", SWITCH_FALSE, SWITCH_FALSE, &err, SWITCH_LOADABLE_MODULE_TYPE_COMMON, event_hash);
 #ifdef SWITCH_HAVE_YUV
 #ifdef SWITCH_HAVE_VPX
-	switch_loadable_module_load_module("", "CORE_VPX_MODULE", SWITCH_FALSE, &err);
+	switch_loadable_module_load_module_ex("", "CORE_VPX_MODULE", SWITCH_FALSE, SWITCH_FALSE, &err, SWITCH_LOADABLE_MODULE_TYPE_COMMON, event_hash);
 #endif
 #endif
 
+	if ((xml = switch_xml_open_cfg(precf, &cfg, NULL))) {
+		switch_xml_t mods, ld;
+		if ((mods = switch_xml_child(cfg, "modules"))) {
+			for (ld = switch_xml_child(mods, "load"); ld; ld = ld->next) {
+				switch_bool_t global = SWITCH_FALSE;
+				const char *val = switch_xml_attr_soft(ld, "module");
+				const char *path = switch_xml_attr_soft(ld, "path");
+				const char *critical = switch_xml_attr_soft(ld, "critical");
+				const char *sglobal = switch_xml_attr_soft(ld, "global");
+
+				if (zstr(val) || (strchr(val, '.') && !strstr(val, ext) && !strstr(val, EXT))) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Invalid extension for %s\n", val);
+					continue;
+				}
+				global = switch_true(sglobal);
+
+				if (path && zstr(path)) {
+					path = SWITCH_GLOBAL_dirs.mod_dir;
+				}
+				if (switch_loadable_module_load_module_ex((char *)path, (char *)val, SWITCH_FALSE, global, &err, SWITCH_LOADABLE_MODULE_TYPE_PRELOAD, event_hash) == SWITCH_STATUS_GENERR) {
+					if (critical && switch_true(critical)) {
+						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load critical module '%s', abort()\n", val);
+
+						if ((hash_val = switch_core_hash_find(event_hash, "0"))) {
+							switch_safe_free(hash_val);
+						}
+						switch_core_hash_destroy(&event_hash);
+
+						abort();
+					}
+				}
+				count++;
+			}
+		}
+		switch_xml_free(xml);
+
+	}
+	else {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "open of %s failed\n", cf);
+	}
+
+	if (switch_core_sqldb_init(&err) != SWITCH_STATUS_SUCCESS)
+	{
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Loading modules interrupted. [Error: %s]\n", err);
+		if ((hash_val = switch_core_hash_find(event_hash, "0"))) {
+			switch_safe_free(hash_val);
+		}
+		switch_core_hash_destroy(&event_hash);
+		return SWITCH_STATUS_GENERR;
+	}
+
+	/* sqldb is ready. Fire holding events! */
+	if ((hash_val = switch_core_hash_find(event_hash, "0"))) {
+		switch_safe_free(hash_val);
+		switch_core_hash_delete(event_hash, "0");
+	}
+
+	for (hi = switch_core_hash_first(event_hash); hi; hi = switch_core_hash_next(&hi)) {
+		switch_core_hash_this(hi, NULL, NULL, &hash_val);
+		event = (switch_event_t *)hash_val;
+		switch_event_fire(&event);
+	}
+
+	switch_core_hash_destroy(&event_hash);
+
 	if ((xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
 		switch_xml_t mods, ld;
 		if ((mods = switch_xml_child(cfg, "modules"))) {
@@ -1902,7 +2270,7 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_init(switch_bool_t autolo
 				if (path && zstr(path)) {
 					path = SWITCH_GLOBAL_dirs.mod_dir;
 				}
-				if (switch_loadable_module_load_module_ex(path, val, SWITCH_FALSE, global, &err) == SWITCH_STATUS_GENERR) {
+				if (switch_loadable_module_load_module_ex(path, val, SWITCH_FALSE, global, &err, SWITCH_LOADABLE_MODULE_TYPE_COMMON, NULL) == SWITCH_STATUS_GENERR) {
 					if (critical && switch_true(critical)) {
 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load critical module '%s', abort()\n", val);
 						abort();
@@ -1935,7 +2303,7 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_init(switch_bool_t autolo
 				if (path && zstr(path)) {
 					path = SWITCH_GLOBAL_dirs.mod_dir;
 				}
-				switch_loadable_module_load_module_ex(path, val, SWITCH_FALSE, global, &err);
+				switch_loadable_module_load_module_ex(path, val, SWITCH_FALSE, global, &err, SWITCH_LOADABLE_MODULE_TYPE_POSTLOAD, NULL);
 				count++;
 			}
 		}
@@ -2047,6 +2415,7 @@ SWITCH_DECLARE(void) switch_loadable_module_shutdown(void)
 {
 	switch_hash_index_t *hi;
 	void *val;
+	const void *key;
 	switch_loadable_module_t *module;
 	int i;
 
@@ -2068,19 +2437,47 @@ SWITCH_DECLARE(void) switch_loadable_module_shutdown(void)
 
 	for (hi = switch_core_hash_first(loadable_modules.module_hash); hi; hi = switch_core_hash_next(&hi)) {
 		switch_core_hash_this(hi, NULL, NULL, &val);
-		module = (switch_loadable_module_t *) val;
-		if (!module->perm) {
+		module = (switch_loadable_module_t *)val;
+		if (module->type != SWITCH_LOADABLE_MODULE_TYPE_PRELOAD && !module->perm) {
 			do_shutdown(module, SWITCH_TRUE, SWITCH_FALSE, SWITCH_FALSE, NULL);
 		}
 	}
 
 	switch_yield(1000000);
 
+	for (hi = switch_core_hash_first(loadable_modules.module_hash); hi;) {
+		switch_core_hash_this(hi, &key, NULL, &val);
+		module = (switch_loadable_module_t *)val;
+
+		hi = switch_core_hash_next(&hi);
+
+		if (module->type != SWITCH_LOADABLE_MODULE_TYPE_PRELOAD && !module->perm) {
+			if (do_shutdown(module, SWITCH_FALSE, SWITCH_TRUE, SWITCH_FALSE, NULL) == SWITCH_STATUS_SUCCESS)
+			{
+				switch_core_hash_delete(loadable_modules.module_hash, key);
+			}
+		}
+	}
+
+	switch_core_sqldb_destroy();
+
+	for (hi = switch_core_hash_first(loadable_modules.module_hash); hi; hi = switch_core_hash_next(&hi)) {
+		switch_core_hash_this(hi, NULL, NULL, &val);
+		if ((module = (switch_loadable_module_t *)val)) {
+			if (module->type == SWITCH_LOADABLE_MODULE_TYPE_PRELOAD && !module->perm) {
+				do_shutdown(module, SWITCH_TRUE, SWITCH_FALSE, SWITCH_FALSE, NULL);
+			}
+		}
+	}
+
+	switch_yield(1000000);
+
 	for (hi = switch_core_hash_first(loadable_modules.module_hash); hi; hi = switch_core_hash_next(&hi)) {
 		switch_core_hash_this(hi, NULL, NULL, &val);
-		module = (switch_loadable_module_t *) val;
-		if (!module->perm) {
-			do_shutdown(module, SWITCH_FALSE, SWITCH_TRUE, SWITCH_FALSE, NULL);
+		if ((module = (switch_loadable_module_t *)val)) {
+			if (module->type == SWITCH_LOADABLE_MODULE_TYPE_PRELOAD && !module->perm) {
+				do_shutdown(module, SWITCH_FALSE, SWITCH_TRUE, SWITCH_FALSE, NULL);
+			}
 		}
 	}
 
@@ -2100,6 +2497,7 @@ SWITCH_DECLARE(void) switch_loadable_module_shutdown(void)
 	switch_core_hash_destroy(&loadable_modules.say_hash);
 	switch_core_hash_destroy(&loadable_modules.management_hash);
 	switch_core_hash_destroy(&loadable_modules.limit_hash);
+	switch_core_hash_destroy(&loadable_modules.database_hash);
 	switch_core_hash_destroy(&loadable_modules.dialplan_hash);
 
 	switch_core_destroy_memory_pool(&loadable_modules.pool);
@@ -2147,6 +2545,34 @@ SWITCH_DECLARE(switch_file_interface_t *) switch_loadable_module_get_file_interf
 	return i;
 }
 
+SWITCH_DECLARE(switch_database_interface_t *) switch_loadable_module_get_database_interface(const char *name, const char *modname)
+{
+	switch_database_interface_t *i = NULL;
+	switch_database_node_t *node, *head;
+
+	switch_mutex_lock(loadable_modules.mutex);
+
+	if ((head = switch_core_hash_find(loadable_modules.database_hash, name))) {
+		if (modname) {
+			for (node = head; node; node = node->next) {
+				if (!strcasecmp(node->interface_name, modname)) {
+					i = (switch_database_interface_t *)node->ptr;
+					break;
+				}
+			}
+		}
+		else {
+			i = (switch_database_interface_t *)head->ptr;
+		}
+	}
+
+	switch_mutex_unlock(loadable_modules.mutex);
+
+	if (i) PROTECT_INTERFACE(i);
+
+	return i;
+}
+
 SWITCH_DECLARE(switch_codec_interface_t *) switch_loadable_module_get_codec_interface(const char *name, const char *modname)
 {
 	switch_codec_interface_t *codec = NULL;
@@ -2711,6 +3137,9 @@ SWITCH_DECLARE(void *) switch_loadable_module_create_interface(switch_loadable_m
 	case SWITCH_LIMIT_INTERFACE:
 		ALLOC_INTERFACE(limit)
 
+	case SWITCH_DATABASE_INTERFACE:
+		ALLOC_INTERFACE(database)
+
 	default:
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Module Type!\n");
 		return NULL;