diff --git a/configs/databases.cfg b/configs/databases.cfg
new file mode 100644
index 00000000..5328927d
--- /dev/null
+++ b/configs/databases.cfg
@@ -0,0 +1,15 @@
+"Databases"
+{
+	"driver_default"		"mysql"
+	
+	"default"
+	{
+		"driver"			"default"
+		"host"				"localhost"
+		"database"			"sourcemod"
+		"user"				"root"
+		"pass"				""
+		//"timeout"			"0"
+		//"port"			"0"
+	}
+}
diff --git a/core/Database.cpp b/core/Database.cpp
index 1a778a9c..d0ffbac7 100644
--- a/core/Database.cpp
+++ b/core/Database.cpp
@@ -16,9 +16,23 @@
 #include "HandleSys.h"
 #include "ShareSys.h"
 #include "sourcemod.h"
+#include "sm_stringutil.h"
+#include "TextParsers.h"
+#include "Logger.h"
+#include "ExtensionSys.h"
+#include <stdlib.h>
+
+#define DBPARSE_LEVEL_NONE		0
+#define DBPARSE_LEVEL_MAIN		1
+#define DBPARSE_LEVEL_DATABASE	2
 
 DBManager g_DBMan;
 
+DBManager::DBManager() 
+: m_StrTab(512), m_ParseLevel(0), m_ParseState(0), m_pDefault(NULL)
+{
+}
+
 void DBManager::OnSourceModAllInitialized()
 {
 	HandleAccess sec;
@@ -31,6 +45,23 @@ void DBManager::OnSourceModAllInitialized()
 	m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
 
 	g_ShareSys.AddInterface(NULL, this);
+
+	g_SourceMod.BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
+}
+
+void DBManager::OnSourceModLevelChange(const char *mapName)
+{
+	SMCParseError err;
+	unsigned int line = 0;
+	if ((err = g_TextParser.ParseFile_SMC(m_Filename, this, &line, NULL)) != SMCParse_Okay)
+	{
+		g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
+		if (err != SMCParse_Custom)
+		{
+			const char *txt = g_TextParser.GetSMCErrorString(err);
+			g_Logger.LogError("[SM] Line %d: %s", line, txt);
+		}
+	}
 }
 
 void DBManager::OnSourceModShutdown()
@@ -64,23 +95,173 @@ void DBManager::OnHandleDestroy(HandleType_t type, void *object)
 	}
 }
 
-bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
+void DBManager::ReadSMC_ParseStart()
 {
-	const DatabaseInfo *pInfo = FindDatabaseConf(name);
+	m_confs.clear();
+	m_ParseLevel = 0;
+	m_ParseState = DBPARSE_LEVEL_NONE;
+	m_StrTab.Reset();
+	m_DefDriver.clear();
+}
 
-	for (size_t i=0; i<m_drivers.size(); i++)
+ConfDbInfo s_CurInfo;
+SMCParseResult DBManager::ReadSMC_NewSection(const char *name, bool opt_quotes)
+{
+	if (m_ParseLevel)
 	{
-		if (strcasecmp(pInfo->driver, m_drivers[i]->GetIdentifier()) == 0)
+		m_ParseLevel++;
+		return SMCParse_Continue;
+	}
+
+	if (m_ParseState == DBPARSE_LEVEL_NONE)
+	{
+		if (strcmp(name, "Databases") == 0)
 		{
-			*pdr = m_drivers[i];
-			*pdb = m_drivers[i]->Connect(pInfo, persistent, error, maxlength);
-			return (*pdb == NULL);
+			m_ParseState = DBPARSE_LEVEL_MAIN;
+		} else {
+			m_ParseLevel++;
+		}
+	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
+		s_CurInfo = ConfDbInfo();
+		s_CurInfo.database = m_StrTab.AddString(name);
+		m_ParseState = DBPARSE_LEVEL_DATABASE;
+	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
+		m_ParseLevel++;
+	}
+
+	return SMCParse_Continue;
+}
+
+SMCParseResult DBManager::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
+{
+	if (m_ParseLevel)
+	{
+		return SMCParse_Continue;
+	}
+
+	if (m_ParseState == DBPARSE_LEVEL_MAIN)
+	{
+		if (strcmp(key, "driver_default") == 0)
+		{
+			m_DefDriver.assign(value);
+		}
+	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
+		if (strcmp(key, "driver") == 0)
+		{
+			if (strcmp(value, "default") != 0)
+			{
+				s_CurInfo.driver = m_StrTab.AddString(value);
+			}
+		} else if (strcmp(key, "database") == 0) {
+			s_CurInfo.database = m_StrTab.AddString(value);
+		} else if (strcmp(key, "host") == 0) {
+			s_CurInfo.host = m_StrTab.AddString(value);
+		} else if (strcmp(key, "user") == 0) {
+			s_CurInfo.user = m_StrTab.AddString(value);
+		} else if (strcmp(key, "pass") == 0) {
+			s_CurInfo.pass = m_StrTab.AddString(value);
+		} else if (strcmp(key, "timeout") == 0) {
+			s_CurInfo.info.maxTimeout = atoi(value);
+		} else if (strcmp(key, "port") == 0) {
+			s_CurInfo.info.port = atoi(value);
 		}
 	}
 
-	*pdr = NULL;
+	return SMCParse_Continue;
+}
+
+#define ASSIGN_VAR(var) \
+	if (s_CurInfo.var == -1) { \
+		s_CurInfo.info.var = ""; \
+	} else { \
+		s_CurInfo.info.var = m_StrTab.GetString(s_CurInfo.var); \
+	}
+
+SMCParseResult DBManager::ReadSMC_LeavingSection()
+{
+	if (m_ParseLevel)
+	{
+		m_ParseLevel--;
+		return SMCParse_Continue;
+	}
+
+	if (m_ParseState == DBPARSE_LEVEL_DATABASE)
+	{
+		/* Set all of the info members to either a blank string
+		 * or the string pointer from the string table.
+		 */
+		ASSIGN_VAR(driver);
+		ASSIGN_VAR(database);
+		ASSIGN_VAR(host);
+		ASSIGN_VAR(user);
+		ASSIGN_VAR(pass);
+		
+		/* Save it.. */
+		m_confs.push_back(s_CurInfo);
+
+		/* Go up one level */
+		m_ParseState = DBPARSE_LEVEL_MAIN;
+	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
+		m_ParseState = DBPARSE_LEVEL_NONE;
+		return SMCParse_Halt;
+	}
+
+	return SMCParse_Continue;
+}
+#undef ASSIGN_VAR
+
+void DBManager::ReadSMC_ParseEnd(bool halted, bool failed)
+{
+}
+
+bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
+{
+	ConfDbInfo *pInfo = GetDatabaseConf(name);
+
+	if (!pInfo)
+	{
+		if (pdr)
+		{
+			*pdr = NULL;
+		}
+		*pdb = NULL;
+		UTIL_Format(error, maxlength, "Configuration \"%s\" not found", name);
+		return false;
+	}
+
+	if (!pInfo->realDriver)
+	{
+		/* Try to assign a real driver pointer */
+		if (pInfo->info.driver[0] == '\0')
+		{
+			if (!m_pDefault && m_DefDriver.size() > 0)
+			{
+				m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
+			}
+			pInfo->realDriver = m_pDefault;
+		} else {
+			pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
+		}
+	}
+
+	if (pInfo->realDriver)
+	{
+		if (pdr)
+		{
+			*pdr = pInfo->realDriver;
+		}
+		*pdb = pInfo->realDriver->Connect(&pInfo->info, persistent, error, maxlength);
+		return (*pdb != NULL);
+	}
+
+	if (pdr)
+	{
+		*pdr = NULL;
+	}
 	*pdb = NULL;
 
+	UTIL_Format(error, maxlength, "Driver \"%s\" not found", pInfo->driver);
+
 	return false;
 }
 
@@ -99,6 +280,27 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
 			return;
 		}
 	}
+
+	/* Make sure NOTHING references this! */
+	List<ConfDbInfo>::iterator iter;
+	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
+	{
+		ConfDbInfo &db = (*iter);
+		if (db.realDriver == pDriver)
+		{
+			db.realDriver = NULL;
+		}
+	}
+}
+
+IDBDriver *DBManager::GetDefaultDriver()
+{
+	if (!m_pDefault && m_DefDriver.size() > 0)
+	{
+		m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
+	}
+
+	return m_pDefault;
 }
 
 Handle_t DBManager::CreateHandle(DBHandleType dtype, void *ptr, IdentityToken_t *pToken)
@@ -158,6 +360,58 @@ IDBDriver *DBManager::GetDriver(unsigned int index)
 
 const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
 {
-	/* :TODO: */
+	ConfDbInfo *info = GetDatabaseConf(name);
+	if (!info)
+	{
+		return NULL;
+	}
+
+	return &info->info;
+}
+
+ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
+{
+	List<ConfDbInfo>::iterator iter;
+
+	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
+	{
+		ConfDbInfo &conf = (*iter);
+		if (strcmp(m_StrTab.GetString(conf.name), name) == 0)
+		{
+			return &conf;
+		}
+	}
+
+	return NULL;
+}
+
+IDBDriver *DBManager::FindOrLoadDriver(const char *name)
+{
+	size_t last_size = m_drivers.size();
+	for (size_t i=0; i<last_size; i++)
+	{
+		if (strcmp(m_drivers[i]->GetIdentifier(), name) == 0)
+		{
+			return m_drivers[i];
+		}
+	}
+
+	char filename[PLATFORM_MAX_PATH];
+	UTIL_Format(filename, sizeof(filename), "dbi.%s", name);
+
+	IExtension *pExt = g_Extensions.LoadAutoExtension(filename);
+	if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size)
+	{
+		return NULL;
+	}
+
+	/* last_size is now gauranteed to be a valid index.
+	 * The identifier must match the name.
+	 */
+	if (strcmp(m_drivers[last_size]->GetIdentifier(), name) == 0)
+	{
+		return m_drivers[last_size];
+	}
+
 	return NULL;
 }
diff --git a/core/Database.h b/core/Database.h
index 47796e39..fa14c553 100644
--- a/core/Database.h
+++ b/core/Database.h
@@ -18,19 +18,43 @@
 #include <IDBDriver.h>
 #include "sm_globals.h"
 #include <sh_vector.h>
+#include <sh_string.h>
+#include <sh_list.h>
+#include <ITextParsers.h>
+#include "sm_memtable.h"
 
 using namespace SourceHook;
 
+struct ConfDbInfo
+{
+	ConfDbInfo() : name(-1), driver(-1), host(-1), user(-1), pass(-1), 
+		database(-1), realDriver(NULL)
+	{
+	}
+	int name;
+	int driver;
+	int host;
+	int user;
+	int pass;
+	int database;
+	IDBDriver *realDriver;
+	DatabaseInfo info;
+};
+
 class DBManager : 
 	public IDBManager,
 	public SMGlobalClass,
-	public IHandleTypeDispatch
+	public IHandleTypeDispatch,
+	public ITextListener_SMC
 {
+public:
+	DBManager();
 public:
 	const char *GetInterfaceName();
 	unsigned int GetInterfaceVersion();
 public: //SMGlobalClass
 	void OnSourceModAllInitialized();
+	void OnSourceModLevelChange(const char *mapName);
 	void OnSourceModShutdown();
 public: //IHandleTypeDispatch
 	void OnHandleDestroy(HandleType_t type, void *object);
@@ -44,10 +68,27 @@ public: //IDBManager
 	Handle_t CreateHandle(DBHandleType type, void *ptr, IdentityToken_t *pToken);
 	HandleError ReadHandle(Handle_t hndl, DBHandleType type, void **ptr);
 	HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token);
+public: //ITextListener_SMC
+	void ReadSMC_ParseStart();
+	SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes);
+	SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes);
+	SMCParseResult ReadSMC_LeavingSection();
+	void ReadSMC_ParseEnd(bool halted, bool failed);
+public:
+	ConfDbInfo *GetDatabaseConf(const char *name);
+	IDBDriver *FindOrLoadDriver(const char *name);
+	IDBDriver *GetDefaultDriver();
 private:
 	CVector<IDBDriver *> m_drivers;
+	List<ConfDbInfo> m_confs;
 	HandleType_t m_DriverType;
 	HandleType_t m_DatabaseType;
+	String m_DefDriver;
+	BaseStringTable m_StrTab;
+	char m_Filename[PLATFORM_MAX_PATH];
+	unsigned int m_ParseLevel;
+	unsigned int m_ParseState;
+	IDBDriver *m_pDefault;
 };
 
 extern DBManager g_DBMan;
diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj
index 07277577..d00b86ea 100644
--- a/core/msvc8/sourcemod_mm.vcproj
+++ b/core/msvc8/sourcemod_mm.vcproj
@@ -796,6 +796,10 @@
 				RelativePath="..\smn_core.cpp"
 				>
 			</File>
+			<File
+				RelativePath="..\smn_database.cpp"
+				>
+			</File>
 			<File
 				RelativePath="..\smn_datapacks.cpp"
 				>
diff --git a/core/smn_database.cpp b/core/smn_database.cpp
new file mode 100644
index 00000000..d1b8c84b
--- /dev/null
+++ b/core/smn_database.cpp
@@ -0,0 +1,860 @@
+#include "sm_globals.h"
+#include "HandleSys.h"
+#include "Database.h"
+#include "ExtensionSys.h"
+#include "PluginSys.h"
+
+HandleType_t hQueryType;
+HandleType_t hStmtType;
+
+class DatabaseHelpers : 
+	public SMGlobalClass,
+	public IHandleTypeDispatch
+{
+public:
+	virtual void OnSourceModAllInitialized()
+	{
+		HandleAccess acc;
+
+		/* Disable cloning */
+		g_HandleSys.InitAccessDefaults(NULL, &acc);
+		acc.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
+
+		TypeAccess tacc;
+
+		g_HandleSys.InitAccessDefaults(&tacc, NULL);
+		tacc.ident = g_pCoreIdent;
+
+		hQueryType = g_HandleSys.CreateType("IQuery", this, 0, &tacc, &acc, g_pCoreIdent, NULL);
+		hStmtType = g_HandleSys.CreateType("IPreparedQuery", this, hQueryType, &tacc, &acc, NULL, NULL);
+	}
+
+	virtual void OnSourceModShutdown()
+	{
+		g_HandleSys.RemoveType(hStmtType, g_pCoreIdent);
+		g_HandleSys.RemoveType(hQueryType, g_pCoreIdent);
+	}
+
+	virtual void OnHandleDestroy(HandleType_t type, void *object)
+	{
+		if (type == hQueryType)
+		{
+			IQuery *query = (IQuery *)object;
+			query->Destroy();
+		} else if (type == hStmtType) {
+			IPreparedQuery *query = (IPreparedQuery *)object;
+			query->Destroy();
+		}
+	}
+} s_DatabaseNativeHelpers;
+
+//is this safe for stmt handles? i think since it's single inheritance, it always will be.
+inline HandleError ReadQueryHndl(Handle_t hndl, IPluginContext *pContext, IQuery **query)
+{
+	HandleSecurity sec;
+	sec.pOwner = pContext->GetIdentity();
+	sec.pIdentity = g_pCoreIdent;
+
+	return g_HandleSys.ReadHandle(hndl, hQueryType, &sec, (void **)query);
+}
+
+inline HandleError ReadStmtHndl(Handle_t hndl, IPluginContext *pContext, IPreparedQuery **query)
+{
+	HandleSecurity sec;
+	sec.pOwner = pContext->GetIdentity();
+	sec.pIdentity = g_pCoreIdent;
+
+	return g_HandleSys.ReadHandle(hndl, hStmtType, &sec, (void **)query);
+}
+
+inline HandleError ReadDbOrStmtHndl(Handle_t hndl, IPluginContext *pContext, IDatabase **db, IPreparedQuery **query)
+{
+	HandleError err;
+	if ((err = g_DBMan.ReadHandle(hndl, DBHandle_Database, (void **)db)) == HandleError_Type)
+	{
+		*db = NULL;
+		return ReadStmtHndl(hndl, pContext, query);
+	}
+	return err;
+}
+
+static cell_t SQL_Connect(IPluginContext *pContext, const cell_t *params)
+{
+	char *conf, *err;
+
+	size_t maxlength = (size_t)params[4];
+	bool persistent = params[2] ? true : false;
+	pContext->LocalToString(params[1], &conf);
+	pContext->LocalToString(params[3], &err);
+	
+	IDBDriver *driver;
+	IDatabase *db;
+	if (!g_DBMan.Connect(conf, &driver, &db, persistent, err, maxlength))
+	{
+		return BAD_HANDLE;
+	}
+
+	/* HACK! Add us to the dependency list */
+	CExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
+	if (pExt)
+	{
+		g_Extensions.BindChildPlugin(pExt, g_PluginSys.FindPluginByContext(pContext->GetContext()));
+	}
+
+	return db->GetHandle();
+}
+
+static cell_t SQL_ConnectEx(IPluginContext *pContext, const cell_t *params)
+{
+	IDBDriver *driver;
+	if (params[1] == BAD_HANDLE)
+	{
+		if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
+		{
+			return pContext->ThrowNativeError("Could not find any default driver");
+		}
+	} else {
+		HandleError err;
+		if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
+			!= HandleError_None)
+		{
+			return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
+		}
+	}
+ 
+	char *host, *user, *pass, *database, *error;
+	size_t maxlength = (size_t)params[7];
+	bool persistent = params[8] ? true : false;
+	unsigned int port = params[9];
+	unsigned int maxTimeout = params[10];
+	pContext->LocalToString(params[2], &host);
+	pContext->LocalToString(params[3], &user);
+	pContext->LocalToString(params[4], &pass);
+	pContext->LocalToString(params[5], &database);
+	pContext->LocalToString(params[6], &error);
+
+	DatabaseInfo info;
+	info.database = database;
+	info.driver = driver->GetIdentifier();
+	info.host = host;
+	info.maxTimeout = maxTimeout;
+	info.pass = pass;
+	info.port = port;
+	info.user = user;
+	
+	IDatabase *db = driver->Connect(&info, persistent, error, maxlength);
+
+	if (db)
+	{
+		/* HACK! Add us to the dependency list */
+		CExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
+		if (pExt)
+		{
+			g_Extensions.BindChildPlugin(pExt, g_PluginSys.FindPluginByContext(pContext->GetContext()));
+		}
+
+		return db->GetHandle();
+	}
+	
+	return BAD_HANDLE;
+}
+
+static cell_t SQL_GetDriverIdent(IPluginContext *pContext, const cell_t *params)
+{
+	IDBDriver *driver;
+	if (params[1] == BAD_HANDLE)
+	{
+		if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
+		{
+			return pContext->ThrowNativeError("Could not find any default driver");
+		}
+	} else {
+		HandleError err;
+		if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
+			!= HandleError_None)
+		{
+			return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
+		}
+	}
+
+	pContext->StringToLocalUTF8(params[2], params[3], driver->GetIdentifier(), NULL);
+
+	return 1;
+}
+
+static cell_t SQL_GetDriver(IPluginContext *pContext, const cell_t *params)
+{
+	char *name;
+	pContext->LocalToString(params[1], &name);
+
+	IDBDriver *driver = NULL;
+	if (name[0] == '\0')
+	{
+		driver = g_DBMan.GetDefaultDriver();
+	} else {
+		driver = g_DBMan.FindOrLoadDriver(name);
+	}
+
+	return (driver != NULL) ? driver->GetHandle() : BAD_HANDLE;
+}
+
+static cell_t SQL_GetDriverProduct(IPluginContext *pContext, const cell_t *params)
+{
+	IDBDriver *driver;
+	if (params[1] == BAD_HANDLE)
+	{
+		if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
+		{
+			return pContext->ThrowNativeError("Could not find any default driver");
+		}
+	} else {
+		HandleError err;
+		if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
+			!= HandleError_None)
+		{
+			return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
+		}
+	}
+
+	pContext->StringToLocalUTF8(params[2], params[3], driver->GetProductName(), NULL);
+
+	return 1;
+}
+
+static cell_t SQL_GetAffectedRows(IPluginContext *pContext, const cell_t *params)
+{
+	IDatabase *db = NULL;
+	IPreparedQuery *stmt = NULL;
+	HandleError err;
+
+	if ((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid statement or db Handle %x (error: %d)", params[1], err);
+	}
+
+	if (db)
+	{
+		return db->GetAffectedRows();
+	} else if (stmt) {
+		return stmt->GetAffectedRows();
+	}
+
+	return pContext->ThrowNativeError("Unknown error reading db/stmt handles");
+}
+
+static cell_t SQL_GetInsertId(IPluginContext *pContext, const cell_t *params)
+{
+	IDatabase *db = NULL;
+	IPreparedQuery *stmt = NULL;
+	HandleError err;
+
+	if ((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid statement or db Handle %x (error: %d)", params[1], err);
+	}
+
+	if (db)
+	{
+		return db->GetInsertID();
+	} else if (stmt) {
+		return stmt->GetInsertID();
+	}
+
+	return pContext->ThrowNativeError("Unknown error reading db/stmt handles");
+}
+
+static cell_t SQL_GetError(IPluginContext *pContext, const cell_t *params)
+{
+	IDatabase *db = NULL;
+	IPreparedQuery *stmt = NULL;
+	HandleError err;
+
+	if ((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid statement or db Handle %x (error: %d)", params[1], err);
+	}
+
+	const char *error = "";
+	if (db)
+	{
+		error = db->GetError();
+	} else if (stmt) {
+		error = stmt->GetError();
+	}
+
+	if (error[0] == '\0')
+	{
+		return false;
+	}
+
+	pContext->StringToLocalUTF8(params[2], params[3], error, NULL);
+
+	return 1;
+}
+
+static cell_t SQL_QuoteString(IPluginContext *pContext, const cell_t *params)
+{
+	IDatabase *db = NULL;
+	HandleError err;
+
+	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
+		!= HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
+	}
+
+	char *input, *output;
+	size_t maxlength = (size_t)params[4];
+	pContext->LocalToString(params[2], &input);
+	pContext->LocalToString(params[3], &output);
+
+	size_t written;
+	bool s = db->QuoteString(input, output, maxlength, &written);
+
+	cell_t *addr;
+	pContext->LocalToPhysAddr(params[5], &addr);
+	*addr = (cell_t)written;
+
+	return s ? 1 : 0;
+}
+
+static cell_t SQL_FastQuery(IPluginContext *pContext, const cell_t *params)
+{
+	IDatabase *db = NULL;
+	HandleError err;
+
+	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
+		!= HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
+	}
+
+	char *query;
+	pContext->LocalToString(params[2], &query);
+
+	return db->DoSimpleQuery(query) ? 1 : 0;
+}
+
+static cell_t SQL_Query(IPluginContext *pContext, const cell_t *params)
+{
+	IDatabase *db = NULL;
+	HandleError err;
+
+	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
+		!= HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
+	}
+
+	char *query;
+	pContext->LocalToString(params[2], &query);
+
+	IQuery *qr = db->DoQuery(query);
+
+	if (!qr)
+	{
+		return BAD_HANDLE;
+	}
+
+	Handle_t hndl = g_HandleSys.CreateHandle(hQueryType, qr, pContext->GetIdentity(), g_pCoreIdent, NULL);
+	if (hndl == BAD_HANDLE)
+	{
+		qr->Destroy();
+		return BAD_HANDLE;
+	}
+
+	return hndl;
+}
+
+static cell_t SQL_PrepareQuery(IPluginContext *pContext, const cell_t *params)
+{
+	IDatabase *db = NULL;
+	HandleError err;
+
+	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
+		!= HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
+	}
+
+	char *query, *error;
+	size_t maxlength = (size_t)params[4];
+	pContext->LocalToString(params[2], &query);
+	pContext->LocalToString(params[3], &error);
+
+	IPreparedQuery *qr = db->PrepareQuery(query, error, maxlength);
+
+	if (!qr)
+	{
+		return BAD_HANDLE;
+	}
+
+	Handle_t hndl = g_HandleSys.CreateHandle(hStmtType, qr, pContext->GetIdentity(), g_pCoreIdent, NULL);
+	if (hndl == BAD_HANDLE)
+	{
+		qr->Destroy();
+		return BAD_HANDLE;
+	}
+
+	return hndl;
+}
+
+static cell_t SQL_FetchMoreResults(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	return query->FetchMoreResults() ? 1 : 0;
+}
+
+static cell_t SQL_HasResultSet(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	return query->GetResultSet() != NULL ? true : false;
+}
+
+static cell_t SQL_GetRowCount(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return 0;
+	}
+
+	return rs->GetRowCount();
+}
+
+static cell_t SQL_GetFieldCount(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return 0;
+	}
+
+	return rs->GetFieldCount();
+}
+
+static cell_t SQL_FieldNumToName(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return pContext->ThrowNativeError("No current result set");
+	}
+
+	unsigned int field = params[2];
+
+	const char *fldname;
+	if ((fldname = rs->FieldNumToName(field)) == NULL)
+	{
+		return pContext->ThrowNativeError("Invalid field index %d", field);
+	}
+
+	pContext->StringToLocalUTF8(params[3], params[4], fldname, NULL);
+
+	return 1;
+}
+
+static cell_t SQL_FieldNameToNum(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return pContext->ThrowNativeError("No current result set");
+	}
+
+	char *field;
+	pContext->LocalToString(params[2], &field);
+
+	cell_t *num;
+	pContext->LocalToPhysAddr(params[3], &num);
+
+	return rs->FieldNameToNum(field, (unsigned int *)num) ? 1 : 0;
+}
+
+static cell_t SQL_FetchRow(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return pContext->ThrowNativeError("No current result set");
+	}
+
+	return (rs->FetchRow() != NULL) ? true : false;
+}
+
+static cell_t SQL_MoreRows(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return pContext->ThrowNativeError("No current result set");
+	}
+
+	return rs->MoreRows() ? 1 : 0;
+}
+
+static cell_t SQL_Rewind(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return pContext->ThrowNativeError("No current result set");
+	}
+
+	return rs->Rewind() ? 1 : 0;
+}
+
+static cell_t SQL_FetchString(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return pContext->ThrowNativeError("No current result set");
+	}
+
+	IResultRow *row = rs->CurrentRow();
+	if (!row)
+	{
+		return pContext->ThrowNativeError("Current result set has no fetched rows");
+	}
+
+	const char *str;
+	size_t length;
+	DBResult res = row->GetString(params[2], &str, &length);
+
+	if (res == DBVal_Error)
+	{
+		return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
+	} else if (res == DBVal_TypeMismatch) {
+		return pContext->ThrowNativeError("Could not fetch data in field %d as a string", params[2]);
+	}
+
+	pContext->StringToLocalUTF8(params[3], params[4], str, &length);
+
+	cell_t *addr;
+	pContext->LocalToPhysAddr(params[5], &addr);
+	*addr = (cell_t)res;
+
+	return (cell_t)length;
+}
+
+static cell_t SQL_FetchFloat(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return pContext->ThrowNativeError("No current result set");
+	}
+
+	IResultRow *row = rs->CurrentRow();
+	if (!row)
+	{
+		return pContext->ThrowNativeError("Current result set has no fetched rows");
+	}
+
+	float f;
+	DBResult res = row->GetFloat(params[2], &f);
+
+	if (res == DBVal_Error)
+	{
+		return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
+	} else if (res == DBVal_TypeMismatch) {
+		return pContext->ThrowNativeError("Could not fetch data in field %d as a float", params[2]);
+	}
+
+	cell_t *addr;
+	pContext->LocalToPhysAddr(params[5], &addr);
+	*addr = (cell_t)res;
+
+	return sp_ftoc(f);
+}
+
+static cell_t SQL_FetchInt(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return pContext->ThrowNativeError("No current result set");
+	}
+
+	IResultRow *row = rs->CurrentRow();
+	if (!row)
+	{
+		return pContext->ThrowNativeError("Current result set has no fetched rows");
+	}
+
+	int iv;
+	DBResult res = row->GetInt(params[2], &iv);
+
+	if (res == DBVal_Error)
+	{
+		return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
+	} else if (res == DBVal_TypeMismatch) {
+		return pContext->ThrowNativeError("Could not fetch data in field %d as an integer", params[2]);
+	}
+
+	cell_t *addr;
+	pContext->LocalToPhysAddr(params[5], &addr);
+	*addr = (cell_t)res;
+
+	return iv;
+}
+
+static cell_t SQL_IsFieldNull(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return pContext->ThrowNativeError("No current result set");
+	}
+
+	IResultRow *row = rs->CurrentRow();
+	if (!row)
+	{
+		return pContext->ThrowNativeError("Current result set has no fetched rows");
+	}
+
+	if ((unsigned)params[2] >= rs->GetFieldCount())
+	{
+		return pContext->ThrowNativeError("Invalid field index %d", params[2]);
+	}
+
+	return row->IsNull(params[2]) ? 1 : 0;
+}
+
+static cell_t SQL_FetchSize(IPluginContext *pContext, const cell_t *params)
+{
+	IQuery *query;
+	HandleError err;
+
+	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
+	}
+
+	IResultSet *rs = query->GetResultSet();
+	if (!rs)
+	{
+		return pContext->ThrowNativeError("No current result set");
+	}
+
+	IResultRow *row = rs->CurrentRow();
+	if (!row)
+	{
+		return pContext->ThrowNativeError("Current result set has no fetched rows");
+	}
+
+	if ((unsigned)params[2] >= rs->GetFieldCount())
+	{
+		return pContext->ThrowNativeError("Invalid field index %d", params[2]);
+	}
+
+	return row->GetDataSize(params[2]);
+}
+
+static cell_t SQL_BindParamInt(IPluginContext *pContext, const cell_t *params)
+{
+	IPreparedQuery *stmt;
+	HandleError err;
+
+	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
+	}
+
+	if (!stmt->BindParamInt(params[2], params[3], params[4] ? true : false))
+	{
+		return pContext->ThrowNativeError("Could not bind parameter %d as an integer", params[2]);
+	}
+
+	return 1;
+}
+
+static cell_t SQL_BindParamFloat(IPluginContext *pContext, const cell_t *params)
+{
+	IPreparedQuery *stmt;
+	HandleError err;
+
+	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
+	}
+
+	if (!stmt->BindParamFloat(params[2], sp_ctof(params[3])))
+	{
+		return pContext->ThrowNativeError("Could not bind parameter %d as a float", params[2]);
+	}
+
+	return 1;
+}
+
+static cell_t SQL_BindParamString(IPluginContext *pContext, const cell_t *params)
+{
+	IPreparedQuery *stmt;
+	HandleError err;
+
+	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
+	}
+
+	char *str;
+	pContext->LocalToString(params[3], &str);
+
+	if (!stmt->BindParamString(params[2], str, params[4] ? true : false))
+	{
+		return pContext->ThrowNativeError("Could not bind parameter %d as a string", params[2]);
+	}
+
+	return 1;
+}
+
+static cell_t SQL_Execute(IPluginContext *pContext, const cell_t *params)
+{
+	IPreparedQuery *stmt;
+	HandleError err;
+
+	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
+	{
+		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
+	}
+
+	return stmt->Execute() ? 1 : 0;
+}
+
+REGISTER_NATIVES(dbNatives)
+{
+	{"SQL_BindParamInt",		SQL_BindParamInt},
+	{"SQL_BindParamFloat",		SQL_BindParamFloat},
+	{"SQL_BindParamString",		SQL_BindParamString},
+	{"SQL_Connect",				SQL_Connect},
+	{"SQL_ConnectEx",			SQL_ConnectEx},
+	{"SQL_Execute",				SQL_Execute},
+	{"SQL_FastQuery",			SQL_FastQuery},
+	{"SQL_FetchFloat",			SQL_FetchFloat},
+	{"SQL_FetchInt",			SQL_FetchInt},
+	{"SQL_FetchMoreResults",	SQL_FetchMoreResults},
+	{"SQL_FetchRow",			SQL_FetchRow},
+	{"SQL_FetchSize",			SQL_FetchSize},
+	{"SQL_FetchString",			SQL_FetchString},
+	{"SQL_FieldNameToNum",		SQL_FieldNameToNum},
+	{"SQL_FieldNumToName",		SQL_FieldNumToName},
+	{"SQL_GetAffectedRows",		SQL_GetAffectedRows},
+	{"SQL_GetDriver",			SQL_GetDriver},
+	{"SQL_GetDriverIdent",		SQL_GetDriverIdent},
+	{"SQL_GetDriverProduct",	SQL_GetDriverProduct},
+	{"SQL_GetError",			SQL_GetError},
+	{"SQL_GetFieldCount",		SQL_GetFieldCount},
+	{"SQL_GetInsertId",			SQL_GetInsertId},
+	{"SQL_GetRowCount",			SQL_GetRowCount},
+	{"SQL_HasResultSet",		SQL_HasResultSet},
+	{"SQL_IsFieldNull",			SQL_IsFieldNull},
+	{"SQL_MoreRows",			SQL_MoreRows},
+	{"SQL_Query",				SQL_Query},
+	{"SQL_QuoteString",			SQL_QuoteString},
+	{"SQL_PrepareQuery",		SQL_PrepareQuery},
+	{"SQL_Rewind",				SQL_Rewind},
+	{NULL,						NULL},
+};
diff --git a/core/systems/ExtensionSys.cpp b/core/systems/ExtensionSys.cpp
index b09aefd8..3c59add2 100644
--- a/core/systems/ExtensionSys.cpp
+++ b/core/systems/ExtensionSys.cpp
@@ -70,7 +70,7 @@ CExtension::CExtension(const char *filename, char *error, size_t maxlength)
 		m_PlId = g_pMMPlugins->Load(path, g_PLID, already, error, maxlength);
 	}
 
-	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType);
+	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this);
 
 	if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading()))
 	{
@@ -132,7 +132,11 @@ void CExtension::MarkAllLoaded()
 
 void CExtension::AddPlugin(IPlugin *pPlugin)
 {
-	m_Plugins.push_back(pPlugin);
+	/* Unfortunately we have to do this :( */
+	if (m_Plugins.find(pPlugin) != m_Plugins.end())
+	{
+		m_Plugins.push_back(pPlugin);
+	}
 }
 
 void CExtension::RemovePlugin(IPlugin *pPlugin)
@@ -866,3 +870,13 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmd, unsigned int argco
 	g_RootMenu.DrawGenericOption("list", "List extensions");
 	g_RootMenu.DrawGenericOption("unload", "Unload an extension");
 }
+
+CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr)
+{
+	if (ptr->type == g_ExtType)
+	{
+		return (CExtension *)(ptr->ptr);
+	}
+
+	return NULL;
+}
diff --git a/core/systems/ExtensionSys.h b/core/systems/ExtensionSys.h
index e714cb8b..a2336c92 100644
--- a/core/systems/ExtensionSys.h
+++ b/core/systems/ExtensionSys.h
@@ -96,6 +96,8 @@ public:
 	void MarkAllLoaded();
 	void AddDependency(IExtension *pSource, const char *file, bool required, bool autoload);
 	void TryAutoload();
+public:
+	CExtension *GetExtensionFromIdent(IdentityToken_t *ptr);
 private:
 	CExtension *FindByOrder(unsigned int num);
 private:
diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp
index 7b4bb201..d268c474 100644
--- a/core/systems/PluginSys.cpp
+++ b/core/systems/PluginSys.cpp
@@ -91,7 +91,7 @@ void CPlugin::InitIdentity()
 {
 	if (!m_handle)
 	{
-		m_ident = g_ShareSys.CreateIdentity(g_PluginIdent);
+		m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this);
 		m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
 		m_ctx.base->SetIdentity(m_ident);
 	}
diff --git a/core/systems/ShareSys.cpp b/core/systems/ShareSys.cpp
index 3dea7ecd..7dc3b70d 100644
--- a/core/systems/ShareSys.cpp
+++ b/core/systems/ShareSys.cpp
@@ -34,7 +34,7 @@ IdentityToken_t *ShareSystem::CreateCoreIdentity()
 		m_CoreType = CreateIdentType("CORE");
 	}
 
-	return CreateIdentity(m_CoreType);
+	return CreateIdentity(m_CoreType, this);
 }
 
 void ShareSystem::OnSourceModStartup(bool late)
@@ -96,7 +96,7 @@ void ShareSystem::OnHandleDestroy(HandleType_t type, void *object)
 	/* THIS WILL NEVER BE CALLED FOR ANYTHING WITH THE IDENTITY TYPE */
 }
 
-IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type)
+IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type, void *ptr)
 {
 	if (!m_TypeRoot)
 	{
@@ -110,6 +110,8 @@ IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type)
 	sec.pOwner = sec.pIdentity = GetIdentRoot();
 
 	pToken->ident = g_HandleSys.CreateHandleInt(type, NULL, &sec, NULL, NULL, true);
+	pToken->ptr = ptr;
+	pToken->type = type;
 
 	return pToken;
 }
diff --git a/core/systems/ShareSys.h b/core/systems/ShareSys.h
index 4627ad35..439623f0 100644
--- a/core/systems/ShareSys.h
+++ b/core/systems/ShareSys.h
@@ -28,6 +28,8 @@ namespace SourceMod
 	struct IdentityToken_t
 	{
 		Handle_t ident;
+		void *ptr;
+		IdentityType_t type;
 	};
 };
 
@@ -53,7 +55,7 @@ public: //IShareSys
 	void AddNatives(IExtension *myself, const sp_nativeinfo_t *natives);
 	IdentityType_t CreateIdentType(const char *name);
 	IdentityType_t FindIdentType(const char *name);
-	IdentityToken_t *CreateIdentity(IdentityType_t type);
+	IdentityToken_t *CreateIdentity(IdentityType_t type, void *ptr);
 	void DestroyIdentType(IdentityType_t type);
 	void DestroyIdentity(IdentityToken_t *identity);
 	void AddDependency(IExtension *myself, const char *filename, bool require, bool autoload);
diff --git a/plugins/include/dbi.inc b/plugins/include/dbi.inc
new file mode 100644
index 00000000..3393e5e0
--- /dev/null
+++ b/plugins/include/dbi.inc
@@ -0,0 +1,445 @@
+/**
+ * vim: set ts=4 :
+ * ===============================================================
+ * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
+ * ===============================================================
+ *
+ *  This file is part of the SourceMod/SourcePawn SDK.  This file may only be used 
+ * or modified under the Terms and Conditions of its License Agreement, which is found 
+ * in LICENSE.txt.  The Terms and Conditions for making SourceMod extensions/plugins 
+ * may change at any time.  To view the latest information, see:
+ *   http://www.sourcemod.net/license.php
+ *
+ * Version: $Id$
+ */
+ 
+#if defined _dbi_included
+ #endinput
+#endif
+#define _dbi_included
+
+/**
+ * @handle Driver
+ *
+ * Contains information about an SQL driver.
+ */
+ 
+/**
+ * @handle Database
+ *
+ * Contains information about a database connection.
+ */
+ 
+/**
+ * @handle Query
+ *
+ * Contains information about an active query and its 
+ * result sets.
+ */
+ 
+/**
+ * @handle Statement : Query
+ *
+ * Extends a Query Handle and can be used as a Query Handle.
+ * Statement Handles are for prepared queries and contain
+ * their own function for binding parameters.  Statement
+ * Handles can be used instead of database Handles in a few
+ * select functions.
+ */
+
+/**
+ * Describes a database field fetch status.
+ */
+enum DBResult
+{
+	DBVal_Error = 0,		/**< Column number/field is invalid. */
+	DBVal_TypeMismatch = 1,	/**< You cannot retrieve this data with this type. */
+	DBVal_Null = 2,			/**< Field has no data (NULL) */
+	DBVal_Data = 3,			/**< Field has data */
+}
+
+/**
+ * Creates an SQL connection from a named configuration.
+ *
+ * @param confname		Named configuration.
+ * @param persistent	True to re-use a previous persistent connection if
+ *						possible, false otherwise.
+ * @param error			Error buffer.
+ * @param maxlength		Maximum length of the error buffer.
+ * @return				A database connection Handle, or INVALID_HANDLE on failure.
+ */
+native Handle:SQL_Connect(const String:confname[], bool:persistent, String:error[], maxlength);
+
+/**
+ * Creates a default SQL connection.
+ *
+ * @param error			Error buffer.
+ * @param maxlength		Maximum length of the error buffer.
+ * @param persistent	True to re-use a previous persistent connection
+ *						if possible, false otherwise.
+ * @return				A database connection Handle, or INVALID_HANDLE on failure.
+ */
+stock Handle:SQL_DefConnect(String:error[], maxlength, bool:persistent=true)
+{
+	return SQL_Connect("default", persistent, error, maxlength);
+}
+
+/**
+ * Creates an SQL connection from specific parameters.
+ *
+ * @param driver		Driver Handle, or INVALID_HANDLE for default.
+ * @param host			Host name.
+ * @param user			User name.
+ * @param pass			User password.
+ * @param database		Database name.
+ * @param error			Error buffer.
+ * @param maxlength		Maximum length of the error buffer.
+ * @param persistent	True to re-use a previous persistent connection 
+ *						if possible, false otherwise.
+ * @param port			Optional port to specify.
+ * @param maxTimeout	Maximum timeout in seconds if applicable.
+ * @return				A database connection Handle, or INVALID_HANDLE on failure.
+ * @error				Invalid driver Handle other than INVALID_HANDLE.
+ */
+native Handle:SQL_ConnectEx(Handle:driver, 
+							const String:host[],
+							const String:user[], 
+							const String:pass[],
+							const String:database[],
+							String:error[],
+							maxlength,
+							bool:persistent=true,
+							port=0,
+							maxTimeout=0);
+
+/**
+ * Returns a driver Handle from a name string.
+ *
+ * If the driver is not found, SourceMod will attempt
+ * to load an extension named dbi.<name>.ext.[dll|so].
+ *
+ * @param name			Driver identification string, or an empty
+ *						string to return the default driver.
+ * @return				Driver Handle, or INVALID_HANDLE on failure.
+ */
+native Handle:SQL_GetDriver(const String:name[]="");
+
+/**
+ * Retrieves a driver's identification string.
+ *
+ * @param driver		Driver Handle, or INVALID_HANDLE for the default driver.
+ * @param ident			Identification string buffer.
+ * @param maxlength		Maximum length of the buffer.
+ * @noreturn
+ * @error				Invalid Handle other than INVALID_HANDLE.
+ */
+native SQL_GetDriverIdent(Handle:driver, String:ident[], maxlength);
+
+/**
+ * Retrieves a driver's product string.
+ *
+ * @param driver		Driver Handle, or INVALID_HANDLE for the default driver.
+ * @param product		Product string buffer.
+ * @param maxlength		Maximum length of the buffer.
+ * @noreturn
+ * @error				Invalid Handle other than INVALID_HANDLE.
+ */
+native SQL_GetDriverProduct(Handle:driver, String:product[], maxlength);
+
+/**
+ * Returns the number of affected rows from the last query.
+ *
+ * @param hndl			A database OR statement Handle.
+ * @return				Number of rows affected by the last query.
+ * @error				Invalid database or statement Handle.
+ */
+native SQL_GetAffectedRows(Handle:hndl);
+
+/**
+ * Returns the last query's insertion id.
+ *
+ * @param hndl			A database OR statement Handle.
+ * @return				Last query's insertion id.
+ * @error				Invalid database or statement Handle.
+ */
+native SQL_GetInsertId(Handle:hndl);
+
+/**
+ * Returns the error reported by the last query.
+ *
+ * @param hndl			A database OR statement Handle.
+ * @param error			Error buffer.
+ * @param maxlength		Maximum length of the buffer.
+ * @return				True if there was an error, false otherwise.
+ * @error				Invalid database or statement Handle.
+ */
+native bool:SQL_GetError(Handle:hndl, String:error[], maxlength);
+
+/**
+ * Quotes a database string for literal insertion.  This is not needed
+ * for binding strings in prepared statements.
+ *
+ * @param hndl			A database Handle.
+ * @param string		String to quote.
+ * @param buffer		Buffer to store quoted string in.
+ * @param maxlength		Maximum length of the buffer.
+ * @param written		Optionally returns the number of bytes written.
+ * @return				True on success, false if buffer is not big enough.
+ *						The buffer must be at least 2*strlen(string)+1.
+ * @error				Invalid database or statement Handle.
+ */
+native bool:SQL_QuoteString(Handle:database, const String:string[], String:buffer[], maxlength, &written=0);
+
+/**
+ * Executes a query and ignores the result set.
+ *
+ * @param database		A database Handle.
+ * @param query			Query string.
+ * @return				True if query succeeded, false otherwise.  Use
+ *						SQL_GetError to find the last error.
+ * @error				Invalid database Handle.
+ */
+native bool:SQL_FastQuery(Handle:database, const String:query[]);
+
+/**
+ * Executes a simple query and returns a new query Handle for
+ * receiving the results.
+ *
+ * @param database		A database Handle.
+ * @param query			Query string.
+ * @return				A new Query Handle on success, INVALID_HANDLE
+ *						otherwise.  The Handle must be freed with CloseHandle().
+ * @error				Invalid database Handle.
+ */
+native Handle:SQL_Query(Handle:database, const String:query[]);
+
+/**
+ * Creates a new prepared statement query.  Prepared statements can
+ * be executed any number of times.  They can also have placeholder
+ * parameters, similar to variables, which can be bound safely and
+ * securely (for example, you do not need to quote bound strings).
+ *
+ * Statement handles will work in any function that accepts a Query handle.
+ *
+ * @param database		A database Handle.
+ * @param query			Query string.
+ * @param error			Error buffer.
+ * @param maxlength		Maximum size of the error buffer.
+ * @return				A new statement Handle on success, INVALID_HANDLE
+ *						otherwise.  The Handle must be freed with CloseHandle().
+ * @error				Invalid database Handle.
+ */
+native Handle:SQL_PrepareQuery(Handle:database, const String:query[], String:error[], maxlength);
+
+/**
+ * Advances to the next set of results.
+ *
+ * In some SQL implementations, multiple result sets can exist on one query.  
+ * This is possible in MySQL with simple queries when executing a CALL 
+ * query.  If this is the case, all result sets must be processed before
+ * another query is made.
+ *
+ * @param query			A query Handle.
+ * @return				True if there was another result set, false otherwise.
+ * @error				Invalid query Handle.
+ */
+native bool:SQL_FetchMoreResults(Handle:query);
+
+/**
+ * Returns whether or not a result set exists.  This will
+ * return true even if 0 results were returned, but false
+ * on queries like UPDATE, INSERT, or DELETE.
+ *
+ * @param query			A query (or statement) Handle.
+ * @return				True if there is a result set, false otherwise.
+ * @error				Invalid query Handle.
+ */
+native bool:SQL_HasResultSet(Handle:query);
+
+/**
+ * Retrieves the number of rows in the last result set.
+ * 
+ * @param query			A query (or statement) Handle.
+ * @error				Invalid query Handle.
+ */
+native SQL_GetRowCount(Handle:query);
+
+/**
+ * Retrieves the name of a field by index.
+ * 
+ * @param query			A query (or statement) Handle.
+ * @param field			Field number (starting from 0).
+ * @param name			Name buffer.
+ * @param maxlength		Maximum length of the name buffer.
+ * @noreturn
+ * @error				Invalid query Handle, invalid field index, or
+ *						no current result set.
+ */
+native SQL_FieldNumToName(Handle:query, field, String:name[], maxlength);
+
+/**
+ * Retrieves a field index by name.
+ * 
+ * @param query			A query (or statement) Handle.
+ * @param name			Name of the field (case sensitive).
+ * @param field			Variable to store field index in.
+ * @return				True if found, false if not found.
+ * @error				Invalid query Handle or no current result set.
+ */
+native bool:SQL_FieldNameToNum(Handle:query, const String:name[], &field);
+
+/**
+ * Fetches a row from the current result set.  This must be 
+ * successfully called before any results are fetched.
+ *
+ * If this function fails, SQL_MoreResults() can be used to
+ * tell if there was an error or the result set is finished.
+ * 
+ * @param query			A query (or statement) Handle.
+ * @return				True if a row was fetched, false otherwise.
+ * @error				Invalid query Handle.
+ */
+native bool:SQL_FetchRow(Handle:query);
+
+/**
+ * Returns if there are more rows.
+ * 
+ * @param query			A query (or statement) Handle.
+ * @return				True if there are more rows, false otherwise.
+ * @error				Invalid query Handle.
+ */
+native bool:SQL_MoreRows(Handle:query);
+
+/**
+ * Rewinds a result set back to the first result.
+ * 
+ * @param query			A query (or statement) Handle.
+ * @return				True on success, false otherwise.
+ * @error				Invalid query Handle or no current result set.
+ */
+native bool:SQL_Rewind(Handle:query);
+
+/**
+ * Fetches a string from a field in the current row of a result set.  
+ * If the result is NULL, an empty string will be returned.  A NULL 
+ * check can be done with the result parameter, or SQL_IsFieldNull().
+ * 
+ * @param query			A query (or statement) Handle.
+ * @param field			The field index (starting from 0).
+ * @param buffer		String buffer.
+ * @param maxlength		Maximum size of the string buffer.
+ * @param result		Optional variable to store the status of the return value.
+ * @return				Number of bytes written.
+ * @error				Invalid query Handle or field index, invalid
+ *						type conversion requested from the database,
+ *						or no current result set.
+ */
+native SQL_FetchString(Handle:query, field, String:buffer[], maxlength, &DBResult:result=DBVal_Error);
+
+/**
+ * Fetches a float from a field in the current row of a result set.  
+ * If the result is NULL, a value of 0.0 will be returned.  A NULL 
+ * check can be done with the result parameter, or SQL_IsFieldNull().
+ * 
+ * @param query			A query (or statement) Handle.
+ * @param field			The field index (starting from 0).
+ * @param result		Optional variable to store the status of the return value.
+ * @return				A float value.
+ * @error				Invalid query Handle or field index, invalid
+ *						type conversion requested from the database,
+ *						or no current result set.
+ */
+native Float:SQL_FetchFloat(Handle:query, field, &DBResult:result=DBVal_Error);
+
+/**
+ * Fetches an integer from a field in the current row of a result set.  
+ * If the result is NULL, a value of 0 will be returned.  A NULL 
+ * check can be done with the result parameter, or SQL_IsFieldNull().
+ * 
+ * @param query			A query (or statement) Handle.
+ * @param field			The field index (starting from 0).
+ * @param result		Optional variable to store the status of the return value.
+ * @return				An integer value.
+ * @error				Invalid query Handle or field index, invalid
+ *						type conversion requested from the database,
+ *						or no current result set.
+ */
+native SQL_FetchInt(Handle:query, field, &DBResult:result=DBVal_Error);
+
+/**
+ * Returns whether a field's data in the current row of a result set is 
+ * NULL or not.  NULL is an SQL type which means "no data."
+ * 
+ * @param query			A query (or statement) Handle.
+ * @param field			The field index (starting from 0).
+ * @return				True if data is NULL, false otherwise.
+ * @error				Invalid query Handle or field index, or no
+ *						current result set.
+ */
+native bool:SQL_IsFieldNull(Handle:query, field);
+
+/**
+ * Returns the length of a field's data in the current row of a result
+ * set.  This only needs to be called for strings to determine how many
+ * bytes to use.  Note that the return value does not include the null
+ * terminator.
+ * 
+ * @param query			A query (or statement) Handle.
+ * @param field			The field index (starting from 0).
+ * @return				Number of bytes for the field's data size.
+ * @error				Invalid query Handle or field index or no
+ *						current result set.
+ */
+native SQL_FetchSize(Handle:query, field);
+
+/**
+ * Binds a parameter in a prepared statement to a given integer value.
+ * 
+ * @param statement		A statement (prepared query) Handle.
+ * @param param			The parameter index (starting from 0).
+ * @param number		The number to bind.
+ * @param signed		True to bind the number as signed, false to 
+ *						bind it as unsigned.
+ * @noreturn
+ * @error				Invalid statement Handle or parameter index, or
+ *						SQL error.
+ */
+native SQL_BindParamInt(Handle:statement, param, number, bool:signed=true);
+
+/**
+ * Binds a parameter in a prepared statement to a given float value.
+ * 
+ * @param statement		A statement (prepared query) Handle.
+ * @param param			The parameter index (starting from 0).
+ * @param float			The float number to bind.
+ * @noreturn
+ * @error				Invalid statement Handle or parameter index, or
+ *						SQL error.
+ */
+native SQL_BindParamFloat(Handle:statement, param, Float:value);
+
+/**
+ * Binds a parameter in a prepared statement to a given string value.
+ * 
+ * @param statement		A statement (prepared query) Handle.
+ * @param param			The parameter index (starting from 0).
+ * @param value			The string to bind.
+ * @param copy			Whether or not SourceMod should copy the value
+ *						locally if necessary.  If the string contents
+ *						won't change before calling SQL_Execute(), this
+ *						can be set to false for optimization.
+ * @noreturn
+ * @error				Invalid statement Handle or parameter index, or
+ *						SQL error.
+ */
+native SQL_BindParamString(Handle:statement, param, const String:value[], bool:copy);
+
+/**
+ * Executes a prepared statement.  All parameters must be bound beforehand.
+ *
+ * @param statement		A statement (prepared query) Handle.
+ * @return				True on success, false on failure.
+ * @error				Invalid statement Handle.
+ */
+native bool:SQL_Execute(Handle:statement);
+
diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc
index 790f03e1..d49d112a 100644
--- a/plugins/include/sourcemod.inc
+++ b/plugins/include/sourcemod.inc
@@ -44,6 +44,7 @@ struct Plugin
 #include <events>
 #include <functions>
 #include <timers>
+#include <dbi>
 
 enum DialogType
 {
diff --git a/public/IDBDriver.h b/public/IDBDriver.h
index 88d3a31f..b7f77b6b 100644
--- a/public/IDBDriver.h
+++ b/public/IDBDriver.h
@@ -24,7 +24,7 @@
 #include <string.h>
 
 #define SMINTERFACE_DBI_NAME		"IDBI"
-#define SMINTERFACE_DBI_VERSION		1
+#define SMINTERFACE_DBI_VERSION		2
 
 namespace SourceMod
 {
@@ -561,7 +561,8 @@ namespace SourceMod
 		virtual Handle_t GetHandle() =0;
 
 		/**
-		 * @brief Returns the driver's controlling identity.
+		 * @brief Returns the driver's controlling identity (must be the same 
+		 * as from IExtension::GetIdentity).
 		 *
 		 * @return				An IdentityToken_t identity.
 		 */
@@ -601,7 +602,8 @@ namespace SourceMod
 		virtual void RemoveDriver(IDBDriver *pDriver) =0;
 
 		/**
-		 * @brief Searches for database info by name.
+		 * @brief Searches for database info by name.  Both the return pointer
+		 * and all pointers contained therein should be considered volatile.
 		 *
 		 * @param name			Named database info.
 		 * @return				DatabaseInfo pointer.
@@ -648,7 +650,8 @@ namespace SourceMod
 		 * @brief Creates a Handle_t of the IDBDriver type.
 		 *
 		 * @param type			A DBHandleType value.
-		 * @param ptr			A pointer corrresponding to a DBHandleType object.
+		 * @param ptr			A pointer corrresponding to a DBHandleType 
+		 *						object.
 		 * @param token			Identity pointer of the owning identity.
 		 * @return				A new Handle_t handle, or 0 on failure.
 		 */
@@ -673,6 +676,22 @@ namespace SourceMod
 		 * @return				HandleError value.
 		 */
 		virtual HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token) =0;
+
+		/**
+		 * @brief Given a driver name, attempts to find it.  If it is not found, SourceMod
+		 * will attempt to load it.
+		 *
+		 * @param name			Driver identifier name.
+		 * @return				IDBDriver pointer on success, NULL otherwise.
+		 */
+		virtual IDBDriver *FindOrLoadDriver(const char *driver) =0;
+
+		/**
+		 * @brief Returns the default driver, or NULL if none is set.
+		 *
+		 * @return				IDBDriver pointer on success, NULL otherwise.
+		 */
+		virtual IDBDriver *GetDefaultDriver() =0;
 	};
 }
 
diff --git a/public/IShareSys.h b/public/IShareSys.h
index c70912b2..dbb35db0 100644
--- a/public/IShareSys.h
+++ b/public/IShareSys.h
@@ -137,13 +137,14 @@ namespace SourceMod
 		virtual IdentityType_t FindIdentType(const char *name) =0;
 
 		/**
-		 * @brief Creates a new identity token.  This token is guaranteed to be unique
-		 * amongst all other open identities.
+		 * @brief Creates a new identity token.  This token is guaranteed to be 
+		 * unique amongst all other open identities.
 		 *
 		 * @param type			Identity type.
-		 * @return				A new IdentityToken_t identifier.
+		 * @param ptr			Private data pointer (cannot be NULL).
+		 * @return				A new IdentityToken_t pointer, or NULL on failure.
 		 */
-		virtual IdentityToken_t *CreateIdentity(IdentityType_t type) =0;
+		virtual IdentityToken_t *CreateIdentity(IdentityType_t type, void *ptr) =0;
 
 		/** 
 		 * @brief Destroys an identity type.  Note that this will delete any identities
@@ -161,7 +162,6 @@ namespace SourceMod
 		 */
 		virtual void DestroyIdentity(IdentityToken_t *identity) =0;
 
-
 		/**
 		 * @brief Requires an extension.  This tells SourceMod that without this extension,
 		 * your extension should not be loaded.  The name should not include the ".dll" or