From f48263b9d26427d46c1284caffb34f923d9af319 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 29 May 2007 08:32:12 +0000 Subject: [PATCH] posted new IDBDriver API - no natives are implemented yet, MySQL extension coming very shortly --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40864 --- core/Database.cpp | 209 +++++++++++----------- core/Database.h | 26 +-- public/IDBDriver.h | 425 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 456 insertions(+), 204 deletions(-) diff --git a/core/Database.cpp b/core/Database.cpp index 711eac03..40b182d8 100644 --- a/core/Database.cpp +++ b/core/Database.cpp @@ -28,7 +28,22 @@ void DBManager::OnSourceModAllInitialized() m_DriverType = g_HandleSys.CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL); m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL); - m_QueryType = g_HandleSys.CreateType("IQuery", this, 0, NULL, NULL, g_pCoreIdent, NULL); +} + +void DBManager::OnSourceModShutdown() +{ + g_HandleSys.RemoveType(m_DatabaseType, g_pCoreIdent); + g_HandleSys.RemoveType(m_DriverType, g_pCoreIdent); +} + +unsigned int DBManager::GetInterfaceVersion() +{ + return SMINTERFACE_DBI_VERSION; +} + +const char *DBManager::GetInterfaceName() +{ + return SMINTERFACE_DBI_NAME; } void DBManager::OnHandleDestroy(HandleType_t type, void *object) @@ -39,124 +54,24 @@ void DBManager::OnHandleDestroy(HandleType_t type, void *object) return; } - if (g_HandleSys.TypeCheck(type, m_QueryType)) + if (g_HandleSys.TypeCheck(type, m_DatabaseType)) { - IQuery *pQuery = (IQuery *)object; - pQuery->Release(); - } else if (g_HandleSys.TypeCheck(type, m_DatabaseType)) { IDatabase *pdb = (IDatabase *)object; pdb->Close(); } } -void DBManager::AddDriver(IDBDriver *pDriver, IdentityToken_t *ident) -{ - if (!pDriver) - { - return; - } - - if (m_drivers.size() >= HANDLESYS_MAX_SUBTYPES) - { - return; - } - - db_driver *driver = new db_driver; - - driver->driver = pDriver; - driver->ident = ident; - - HandleSecurity sec; - - sec.pIdentity = ident; - sec.pOwner = g_pCoreIdent; - driver->hDriver = g_HandleSys.CreateHandleEx(m_DriverType, driver, &sec, NULL, NULL); - - driver->htDatabase = g_HandleSys.CreateType(NULL, this, m_DatabaseType, NULL, NULL, g_pCoreIdent, NULL); - driver->htQuery = g_HandleSys.CreateType(NULL, this, m_QueryType, NULL, NULL, g_pCoreIdent, NULL); - - m_drivers.push_back(driver); -} - -void DBManager::RemoveDriver(IDBDriver *pDriver) -{ - CVector::iterator iter; - for (iter = m_drivers.begin(); - iter != m_drivers.end(); - iter++) - { - if ((*iter)->driver == pDriver) - { - db_driver *driver = (*iter); - HandleSecurity sec; - - sec.pIdentity = driver->ident; - sec.pOwner = g_pCoreIdent; - g_HandleSys.RemoveType(driver->htQuery, driver->ident); - g_HandleSys.RemoveType(driver->htDatabase, driver->ident); - g_HandleSys.FreeHandle(driver->hDriver, &sec); - - delete driver; - m_drivers.erase(iter); - - return; - } - } -} - -const char *DBManager::GetInterfaceName() -{ - return SMINTERFACE_DBI_NAME; -} - -unsigned int DBManager::GetInterfaceVersion() -{ - return SMINTERFACE_DBI_VERSION; -} - -unsigned int DBManager::GetDriverCount() -{ - return (unsigned int)m_drivers.size(); -} - -IDBDriver *DBManager::GetDriver(unsigned int index, IdentityToken_t **pToken) -{ - if (index >= GetDriverCount()) - { - return NULL; - } - - if (pToken) - { - *pToken = m_drivers[index]->ident; - } - - return m_drivers[index]->driver; -} - -const DatabaseInfo *DBManager::FindDatabaseConf(const char *name) -{ - /* :TODO: implement */ - return NULL; -} - -bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent) +bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength) { const DatabaseInfo *pInfo = FindDatabaseConf(name); - unsigned int count = GetDriverCount(); - for (unsigned int i=0; iGetIdentifier(), pInfo->driver) == 0) + if (strcasecmp(pInfo->driver, m_drivers[i]->GetIdentifier()) == 0) { - *pdr = driver; - if (persistent) - { - *pdb = driver->PConnect(pInfo); - } else { - *pdb = driver->Connect(pInfo); - } + *pdr = m_drivers[i]; + *pdb = m_drivers[i]->Connect(pInfo, persistent, error, maxlength); + return (*pdb == NULL); } } @@ -165,3 +80,81 @@ bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool return false; } + +void DBManager::AddDriver(IDBDriver *pDriver) +{ + m_drivers.push_back(pDriver); +} + +void DBManager::RemoveDriver(IDBDriver *pDriver) +{ + for (size_t i=0; i= m_drivers.size()) + { + return NULL; + } + + return m_drivers[index]; +} + +const DatabaseInfo *DBManager::FindDatabaseConf(const char *name) +{ + /* :TODO: */ + return NULL; +} diff --git a/core/Database.h b/core/Database.h index 9e462f5c..47796e39 100644 --- a/core/Database.h +++ b/core/Database.h @@ -16,46 +16,38 @@ #define _INCLUDE_DATABASE_MANAGER_H_ #include -#include #include "sm_globals.h" #include using namespace SourceHook; -struct db_driver -{ - IDBDriver *driver; - IdentityToken_t *ident; - HandleType_t htDatabase; - HandleType_t htQuery; - Handle_t hDriver; -}; - class DBManager : public IDBManager, public SMGlobalClass, public IHandleTypeDispatch { +public: const char *GetInterfaceName(); unsigned int GetInterfaceVersion(); public: //SMGlobalClass void OnSourceModAllInitialized(); + void OnSourceModShutdown(); public: //IHandleTypeDispatch void OnHandleDestroy(HandleType_t type, void *object); public: //IDBManager - void AddDriver(IDBDriver *pDriver, IdentityToken_t *ident); + void AddDriver(IDBDriver *pDrivera); void RemoveDriver(IDBDriver *pDriver); const DatabaseInfo *FindDatabaseConf(const char *name); - bool Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent); + bool Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength); unsigned int GetDriverCount(); - IDBDriver *GetDriver(unsigned int index, IdentityToken_t **pToken=NULL); -public: - db_driver *GetDriverInfo(unsigned int index); + IDBDriver *GetDriver(unsigned int index); + 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); private: - CVector m_drivers; + CVector m_drivers; HandleType_t m_DriverType; HandleType_t m_DatabaseType; - HandleType_t m_QueryType; }; extern DBManager g_DBMan; diff --git a/public/IDBDriver.h b/public/IDBDriver.h index f79e4843..130ea6d1 100644 --- a/public/IDBDriver.h +++ b/public/IDBDriver.h @@ -20,6 +20,7 @@ #define _INCLUDE_SOURCEMOD_INTERFACE_DBDRIVER_H_ #include +#include #include #define SMINTERFACE_DBI_NAME "IDBI" @@ -38,8 +39,27 @@ namespace SourceMod DBVal_Data = 3, /**< Field has data */ }; + /** + * @brief Describes a primitive database type. + */ + enum DBType + { + DBType_Unknown = 0, /**< Type could not be inferred */ + DBType_String, /**< NULL-terminated string (variable length) */ + DBType_Blob, /**< Raw binary data (variable length) */ + DBType_Integer, /**< 4-byte signed integer */ + DBType_Float, /**< 4-byte floating point */ + }; + /** * @brief Represents a one database result row. + * + * Note that type mismatches will only occur when type safety is being + * enforced. So far this is only the case for prepared statements in + * MySQL and SQLite. + * + * Also, it is worth noting that retrieving as raw data will never cause a + * type mismatch. */ class IResultRow { @@ -47,14 +67,33 @@ namespace SourceMod /** * @brief Retrieves a database field result as a string. * - * For NULL values, the resulting string pointer will be non-NULL, - * but empty. + * For NULL values, the resulting string pointer will be non-NULL but + * empty. The pointer returned will become invalid after advancing to + * the next row. * * @param columnId Column to use, starting from 0. * @param pString Pointer to store a pointer to the string. + * @param length Optional pointer to store the string length. * @return A DBResult return code. */ - virtual DBResult GetString(unsigned int columnId, const char **pString) =0; + virtual DBResult GetString(unsigned int columnId, const char **pString, size_t *length) =0; + + /** + * @brief Retrieves a database field result as a string, using a + * user-supplied buffer. If the field is NULL, an empty string + * will be copied. + * + * @param columnId Column to use, starting from 0. + * @param buffer Buffer to store string in. + * @param maxlength Maximum length of the buffer. + * @param written Optional pointer to store the number of bytes + * written, excluding the null terminator. + * @return A DBResult return code. + */ + virtual DBResult CopyString(unsigned int columnId, + char *buffer, + size_t maxlength, + size_t *written) =0; /** * @brief Retrieves a database field result as a float. @@ -82,19 +121,51 @@ namespace SourceMod * @brief Returns whether or not a field is NULL. * * @param columnId Column to use, starting from 0. - * @return A DBResult return code. + * @return True if field is NULL, false otherwise. */ - virtual DBResult CheckField(unsigned int columnId) =0; + virtual bool IsNull(unsigned int columnId) =0; /** - * @brief Retrieves field data as a raw bitstream. + * @brief Returns the size of a field (text/raw/blob) in bytes. + * For strings, this returned size will not include the null + * terminator. + * + * When used on fields that are not of variable length, + * the size returned will be the number of bytes required + * to store the internal data. Note that the data size + * will correspond to the ACTUAL data type, not the + * COLUMN type. * * @param columnId Column to use, starting from 0. - * @param pData Pointer to store the raw bit stream. + * @return Number of bytes required to store + * the data, or 0 on failure. + */ + virtual size_t GetDataSize(unsigned int columnId) =0; + + /** + * @brief Retrieves field data as a raw bitstream. The pointer returned + * will become invalid after advancing to the next row. + * + * @param columnId Column to use, starting from 0. + * @param pData Pointer to store the raw bit stream. If the + * data is NULL, a NULL pointer will be returned. * @param length Pointer to store the data length. * @return A DBResult return code. */ - virtual DBResult GetRaw(unsigned int columnId, void **pData, size_t *length) =0; + virtual DBResult GetBlob(unsigned int columnId, const void **pData, size_t *length) =0; + + /** + * @brief Copies field data as a raw bitstream. + * + * @param columnId Column to use, starting from 0. + * @param buffer Pointer to copy the data to. If the data is + * NULL, no data will be copied. + * @param maxlength Maximum length of the buffer. + * @param written Optional pointer to store the number of bytes + * written. + * @return A DBResult return code. + */ + virtual DBResult CopyBlob(unsigned int columnId, void *buffer, size_t maxlength, size_t *written) =0; }; /** @@ -123,7 +194,7 @@ namespace SourceMod * @param columnId Column to use, starting from 0. * @return Pointer to column name, or NULL if not found. */ - virtual const char *ColNumToName(unsigned int columnId) =0; + virtual const char *FieldNumToName(unsigned int columnId) =0; /** * @brief Converts a column name to a column id. @@ -134,7 +205,7 @@ namespace SourceMod * undefined. * @return True on success, false if not found. */ - virtual bool ColNumToName(const char *name, unsigned int *columnId) =0; + virtual bool FieldNameToNum(const char *name, unsigned int *columnId) =0; /** * @brief Returns if there is still data in the result set. @@ -142,7 +213,7 @@ namespace SourceMod * @return False if there is more data to be read, * true, otherwise. */ - virtual bool IsFinished() =0; + virtual bool MoreRows() =0; /** * @brief Returns a pointer to the current row and advances @@ -159,16 +230,149 @@ namespace SourceMod * @return True on success, false otherwise. */ virtual bool Rewind() =0; + + /** + * @brief Returns a field's type as it should be interpreted + * by the user. + * + * @param field Field number (starting from 0). + * @return A DBType value. + */ + virtual DBType GetFieldType(unsigned int field) =0; + + /** + * @brief Returns a field's type as it will be interpreted + * by the GetDataSize() function. For example, MySQL + * for non-prepared queries will store all results as + * strings internally. + * + * @param field Field number (starting from 0). + * @return A DBType value. + */ + virtual DBType GetFieldDataType(unsigned int field) =0; }; - class IDatabase; + class IDBDriver; - /** - * @brief Encapsulates one database query. - */ class IQuery { public: + /** + * @brief Returns a pointer to the current result set, if any. + * + * @return An IResultSet pointer on success, + * NULL if no result set exists. + */ + virtual IResultSet *GetResultSet() =0; + + /** + * @brief Advances to the next result set if one exists. This + * is for checking for MORE result sets, and should not be used + * on the first result set. + * + * Multiple results only happen in certain cases, such as CALLing + * stored procedure that have a SELECTs, where MySQL will return + * both the CALL status and one or more SELECT result sets. If + * you do not process these results, they will be automatically + * processed for you. However, the behaviour of creating a new + * query from the same connection while results are left + * unprocessed is undefined, and may result in a dropped + * connection. Therefore, process all extra results or destroy the + * IQuery pointer before starting a new query. + * + * Again, this only happens in very specific cases, so there is + * no need to call this for normal queries. + * + * After calling this function, GetResultSet() must be called + * again to return the result set. The previous result set + * is automatically destroyed and will be unusable. + * + * @return True if another result set is + * available, false otherwise. + */ + virtual bool FetchMoreResults() =0; + + /** + * @brief Frees resources created by this query. + */ + virtual void Destroy() =0; + }; + + class IPreparedQuery : public IQuery + { + public: + /** + * @brief Binds an integer parameter. + * + * @param param Parameter index, starting from 0. + * @param num Number to bind as a value. + * @param signd True to write as signed, false to write as + * unsigned. + * @return True on success, false otherwise. + */ + virtual bool BindParamInt(unsigned int param, int num, bool signd=true) =0; + + /** + * @brief Binds a float parameter. + * + * @param param Parameter index, starting from 0. + * @param f Float to bind as a value. + * @return True on success, false otherwise. + */ + virtual bool BindParamFloat(unsigned int param, float f) =0; + + /** + * @brief Binds an SQL NULL type as a parameter. + * + * @param param Parameter index, starting from 0. + * @return True on success, false otherwise. + */ + virtual bool BindParamNull(unsigned int param) =0; + + /** + * @brief Binds a string as a parameter. + * + * @param param Parameter index, starting from 0. + * @param text Pointer to a null-terminated string. + * @param copy If true, the pointer is assumed to be + * volatile and a temporary copy may be + * made for safety. + * @return True on success, false otherwise. + */ + virtual bool BindParamString(unsigned int param, const char *text, bool copy) =0; + + /** + * @brief Binds a blob of raw data as a parameter. + * + * @param param Parameter index, starting from 0. + * @param data Pointer to a blob of memory. + * @param length Number of bytes to copy. + * @param copy If true, the pointer is assumed to be + * volatile and a temporary copy may be + * made for safety. + * @return True on success, false otherwise. + */ + virtual bool BindParamBlob(unsigned int param, + const void *data, + size_t length, + bool copy) =0; + + /** + * @brief Executes the query with the currently bound parameters. + + * @return True on success, false otherwise. + */ + virtual bool Execute() =0; + + /** + * @brief Returns the last error message from this statement. + * + * @param errCode Optional pointer to store the driver-specific + * error code. + * @return Error message string. + */ + virtual const char *GetError(int *errCode=NULL) =0; + /** * @brief Number of rows affected by the last execute. * @@ -177,45 +381,13 @@ namespace SourceMod virtual unsigned int GetAffectedRows() =0; /** - * @brief Retrieves the last insert ID of this query, if any. + * @brief Retrieves the last insert ID on this database connection. * * @return Row insertion ID of the last execute, if any. */ virtual unsigned int GetInsertID() =0; - - /** - * @brief Returns the full query string. - * - * @return String containing the original query. - */ - virtual const char *GetQueryString() =0; - - /** - * @brief Returns the result set from the last Execute call. - * - * @return IResultSet from the last Execute call. - * Pointer will be NULL if there was no previous - * call, the last call failed, or the last call - * had no result set and Execute() was called with - * retEmptySet=false. - */ - virtual IResultSet *GetResultSet() =0; - - /** - * @brief Returns the IDatabase object which created this pointer. - * - * @return IDatabase object which created this pointer. - */ - virtual IDatabase *GetDatabase() =0; - - /** - * @brief Releases the resources associated with this query. - */ - virtual void Release() =0; }; - class IDBDriver; - /** * @brief Encapsulates a database connection. */ @@ -224,11 +396,18 @@ namespace SourceMod public: /** * @brief Disconnects the database and frees its associated memory. - * If this pointer was received via IDBDriver::PConnect(), Close() - * must be called once for each successful PConnect() call which - * resulted in this pointer. + * Note that the actual object will not be freed until all open + * references have been closed. + * + * It is guaranteed that an IDatabase pointer won't be destroyed until + * all open IQuery or IPreparedQuery pointers are closed. + * + * @param Only pass true if being called from an + * IHandleTypeDispatch destructor. + * @return True if object was destroyed, false if + * references are remaining. */ - virtual void Close() =0; + virtual bool Close(bool fromHndlSys=false) =0; /** * @brief Error code and string returned by the last operation on this @@ -237,18 +416,41 @@ namespace SourceMod * @param errorCode Optional pointer to retrieve an error code. * @return Error string pointer (empty if none). */ - virtual const char *GetLastError(int *errorCode=NULL) =0; + virtual const char *GetError(int *errorCode=NULL) =0; + + /** + * @brief Prepares and executes a query in one step, and discards + * any return data. + * + * @param query Query string. + * @return True on success, false otherwise. + */ + virtual bool DoSimpleQuery(const char *query) =0; /** * @brief Prepares and executes a query in one step, and returns - * the resulting IQuery pointer. + * the resultant data set. * - * @param fmt String format to parse. - * @param ... Format parameters. - * @return An IQuery pointer which must be freed, or - * NULL if the query failed. + * Note: If a query contains more than one result set, each + * result set must be processed before a new query is started. + * + * @param query Query string. + * @return IQuery pointer on success, NULL otherwise. */ - virtual IQuery *DoQuery(const char *fmt, ...) =0; + virtual IQuery *DoQuery(const char *query) =0; + + /** + * @brief Prepares a query statement for multiple executions and/or + * binding marked parameters (? in MySQL/sqLite, $n in PostgreSQL). + * + * @param query Query string. + * @param error Error buffer. + * @param maxlength Maximum length of the error buffer. + * @param errCode Optional pointer to store a driver-specific error code. + * @return IPreparedQuery pointer on success, NULL + * otherwise. + */ + virtual IPreparedQuery *PrepareQuery(const char *query, char *error, size_t maxlength, int *errCode=NULL) =0; /** * Quotes a string for insertion into a query. @@ -264,11 +466,25 @@ namespace SourceMod virtual bool QuoteString(const char *str, char buffer[], size_t maxlen, size_t *newSize) =0; /** - * @brief Returns the parent driver. + * @brief Number of rows affected by the last execute. * - * @return IDBDriver pointer that created this object. + * @return Number of rows affected by the last execute. */ - virtual IDBDriver *GetDriver() =0; + virtual unsigned int GetAffectedRows() =0; + + /** + * @brief Retrieves the last insert ID on this database connection. + * + * @return Row insertion ID of the last execute, if any. + */ + virtual unsigned int GetInsertID() =0; + + /** + * @brief Returns an IDatabase Handle to this IDatabase. + * + * @return Handle_t of type IDatabase. + */ + virtual Handle_t GetHandle() =0; }; /** @@ -307,35 +523,50 @@ namespace SourceMod * @brief Initiates a database connection. * * @param info Database connection info pointer. + * @param persistent If true, a previous persistent connection will + * be re-used if possible. + * @param error Buffer to store error message. + * @param maxlength Maximum size of the error buffer. * @return A new IDatabase pointer, or NULL on failure. */ - virtual IDatabase *Connect(const DatabaseInfo *info) =0; - - /** - * @brief Initiates a database connection. If a connection to the given database - * already exists, this will re-use the old connection handle. - * - * @param info Database connection info pointer. - * @return A new IDatabase pointer, or NULL on failure. - */ - virtual IDatabase *PConnect(const DatabaseInfo *info) =0; + virtual IDatabase *Connect(const DatabaseInfo *info, bool persistent, char *error, size_t maxlength) =0; /** * @brief Returns a case insensitive database identifier string. + * + * @return String containing an identifier. */ virtual const char *GetIdentifier() =0; /** * @brief Returns a case sensitive implementation name. + * + * @return String containing an implementation name. */ virtual const char *GetProductName() =0; /** - * @brief Returns the last connection error. + * @brief Retrieves a Handle_t handle of the IDBDriver type. * - * @param errCode Optional pointer to store a platform-specific error code. + * @return A Handle_t handle. */ - virtual const char *GetLastError(int *errCode=NULL) =0; + virtual Handle_t GetHandle() =0; + + /** + * @brief Returns the driver's controlling identity. + * + * @return An IdentityToken_t identity. + */ + virtual IdentityToken_t *GetIdentity() =0; + }; + + /** + * @brief Database-related Handle types. + */ + enum DBHandleType + { + DBHandle_Driver = 0, /**< Driver Handle */ + DBHandle_Database = 1, /**< Database Handle */ }; /** @@ -352,7 +583,7 @@ namespace SourceMod * * @param pDriver Database driver. */ - virtual void AddDriver(IDBDriver *pDriver, IdentityToken_t *pToken) =0; + virtual void AddDriver(IDBDriver *pDriver) =0; /** * @brief Removes a driver from the DBI system. @@ -379,9 +610,16 @@ namespace SourceMod * If connection fails, NULL will be stored. * @param persistent If true, the dbmanager will attempt to PConnect * instead of connect. + * @param error Error buffer to store a driver's error message. + * @param maxlength Maximum length of the error buffer. * @return True on success, false otherwise. */ - virtual bool Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent) =0; + virtual bool Connect(const char *name, + IDBDriver **pdr, + IDatabase **pdb, + bool persistent, + char *error, + size_t maxlength) =0; /** * @brief Returns the number of drivers loaded. @@ -394,10 +632,39 @@ namespace SourceMod * @brief Returns a driver by index. * * @param index Driver index, starting from 0. - * @param pToken Optional pointer to store the driver's identity token. * @return IDBDriver pointer for the given index. */ - virtual IDBDriver *GetDriver(unsigned int index, IdentityToken_t **pToken=NULL) =0; + virtual IDBDriver *GetDriver(unsigned int index) =0; + + /** + * @brief Creates a Handle_t of the IDBDriver type. + * + * @param type A DBHandleType value. + * @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. + */ + virtual Handle_t CreateHandle(DBHandleType type, void *ptr, IdentityToken_t *pToken) =0; + + /** + * @brief Reads an IDBDriver pointer from an IDBDriver handle. + * + * @param hndl Handle_t handle to read. + * @param type A DBHandleType value. + * @param ptr Pointer to store the object pointer. + * @return HandleError value. + */ + virtual HandleError ReadHandle(Handle_t hndl, DBHandleType type, void **ptr) =0; + + /** + * @brief Releases an IDBDriver handle. + * + * @param hndl Handle_t handle to release. + * @param type A DBHandleType value. + * @param token Identity pointer of the owning identity. + * @return HandleError value. + */ + virtual HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token) =0; }; }