金仓数据库KingbaseES———DCI初始化模块的处理过程
关键字:
DCI、初始化、连接建立、人大金仓、KingbaseES
DCI初始化环境
初始化环境是连接数据库进行各项操作的前提。OCI的初始化模块包含了初始化OCI全局环境,分配所有句柄的父句柄——环境句柄,再依次分配其他句柄并设置相关属性。各个函数的调用关系如图1所示。
图1 初始化模块函数调用关系
分配和初始化环境句柄有DCIInitialize+DCIEnvInit、DCIEnvCreate两种实现方式,DCIInitialize()和DCIEnvInit()主要是为了兼容早期版本。服务器上下文句柄及其子句柄可以通过DCIHandleAlloc函数显示分配并进行初始化,也可以使用DCILogon()、DCILogon2()隐式分配服务器上下文句柄,并已经初始化。对于数据库连接,仅维护一个单独的用户会话可以调用DCILogon()、DCILogon2()初始化上下文句柄。对于复杂会话管理的应用程序,必须显式分配服务器上下文,并将服务器和用户会话显式设置到上下文句柄。DCIServerAttach( )将数据库名挂载到服务器句柄上(即dblink参数),并不执行实质性的连接工作。具体操作如下:
err = DCIInitialize((ub4) OCI_DEFAULT, (dvoid *)0, (dvoid * (*)(dvoid *, size_t)) 0, (dvoid * (*)(dvoid *, dvoid *, size_t))0, (void (*)(dvoid *, dvoid *)) 0);
if (err != OCI_SUCCESS)
{
printf(“OCIInitialize failed\n”);
return err;
}
err = DCIEnvInit((OCIEnv **) &pEnv, OCI_DEFAULT, 0, NULL);
if (err != OCI_SUCCESS)
{
printf(“OCIEnvInit failed\n”);
return err;
}
err = DCIHandleAlloc((dvoid *) pEnv, (dvoid **) &pError, OCI_HTYPE_ERROR, 0, NULL);
err=DCIHandleAlloc(envhp,(void**)svchp,DCI_HTYPE_SVCCTX,0,NULL);
if(err!=DCI_SUCCESS)
gotoend;
/*AllocateServerHandle*/
err=DCIHandleAlloc(envhp,(dvoid**)&((*svchp)->pServer),DCI_HTYPE_SERVER,(size_t)0,NULL);
if(err!=DCI_SUCCESS)
gotoend;
/*SetServerIntoServerContext*/
err=DCIAttrSet(*svchp,DCI_HTYPE_SVCCTX,(*svchp)->pServer,0,DCI_ATTR_SERVER,errhp);
/*AttachServer*/
err=DCIServerAttach((*svchp)->pServer,errhp,dbname,(sb4)strlen(dbname),DCI_DEFAULT);
/*AllocateSessionHandle*/
err=DCIHandleAlloc(envhp,(dvoid**)&((*svchp)->pSession),(ub4)DCI_HTYPE_SESSION,(size_t)0,NULL);
if(err!=DCI_SUCCESS)
gotoend;
/*SetSessionIntoServerContext*/
err=DCIAttrSet(*svchp,DCI_HTYPE_SVCCTX,(*svchp)->pSession,0,DCI_ATTR_SESSION,errhp);
/*SetSessionUserName*/
err=DCIAttrSet((*svchp)->pSession,DCI_HTYPE_SESSION,(void*)username,(ub4)strlen(username),DCI_ATTR_USERNAME,errhp);
/*SetSessionPassword*/
err=DCIAttrSet((*svchp)->pSession,DCI_HTYPE_SESSION,(void*)password,(ub4)strlen(password),DCI_ATTR_PASSWORD,errhp);
end:
if(err!=DCI_SUCCESS)
{
if(*svchp)
{
if((*svchp)->pSession)
DCIHandleFree((dvoid*)(*svchp)->pSession,DCI_HTYPE_SESSION);
if((*svchp)->pServer)
DCIHandleFree((dvoid*)((*svchp)->pServer),DCI_HTYPE_SERVER);
DCIHandleFree((void*)*svchp,DCI_HTYPE_SVCCTX);
*svchp=NULL;
}
#ifdefNCI_JK
err=DCI_ERROR;
#endif
}
由于句柄申请过程中涉及到动态内存空间的申请,一旦申请失败,就要调用相应的句柄释放函数释放资源,以免造成内存泄漏。
KSAPI_AllocEnv、ENV_Constructor、KSAPI_AllocConnect、CC_Constructor等是ODBC层的句柄申请和空间分配操作,仅处理环境句柄与会话句柄结构体成员EnvironmentClass与ConnectionClass的空间分配。在当前代码中,主要是对ODBC层的句柄申请和分配进行封装,并设置相关错误码,ODBC层的错误码并不出现在DCI分配的错误句柄中,而是由ODBC层的句柄自己携带,进行相应的逻辑判断,只将少量句柄传入DCI层并转化为Oracle的标准错误码。
DCI初始化资源释放
在程序执行结束,与服务器断开连接之后,需要依次释放初始化过程中申请的各类句柄资源,其函数调用关系如图2所示。
图2 初始化资源释放函数调用关系
初始化申请的环境释放过程中,首先应处理服务上下文句柄的三个子句柄:
① 对应DCIServerAttach( ),在服务器句柄释放之前,需要调用DCIServerDetach( )解除服务上下文句柄与数据库的关联,将srvhp->dblink释放并置空;
② 释放会话句柄。由于同一个会话句柄可能会创建多个数据库连接,例如读写分离,负载均衡等,因此在会话句柄释放时需要循环调用KSAPI_FreeConnect( ),以便释放所有连接。连接释放过程涉及到了ODBC层的ConnectionClass相关资源的释放:首先从当前环境中移除连接ENV_remove_connection,若未成功移除,设置错误信息
CC_set_error(conn,CONN_ERR_IN_USE,”Atransactioniscurrentlybeingexecuted.”,func);
之后,在
char CC_Destructor(ConnectionClass*conn)
中调用CC_cleanup清除连接,并且释放conn->__error_message空间。
③释放服务器句柄。
处理完服务上下文相关的子句柄之后,就可依次释放服务上下文句柄、错误句柄和环境句柄。由于环境句柄是所有句柄的父句柄,所以一般在程序最后才进行释放。
具体操作为:
err=DCIServerDetach(pServer,pError,OCI_DEFAULT);
err=DCIHandleFree(pSession,OCI_HTYPE_SESSION);
pSession=NULL;
err=DCIHandleFree((dvoid*)pSvcCtx,(ub4)OCI_HTYPE_SVCCTX);
if(err!=OCI_SUCCESS)
{
printf(“OCIHandleFreeOCI_HTYPE_SVCCTXfailed\n”);
}
err=DCIHandleFree((dvoid*)pError,(ub4)OCI_HTYPE_ERROR);
if(err!=OCI_SUCCESS)
{
printf(“OCIHandleFreeOCI_HTYPE_ERRORfailed\n”);
}
pError=NULL;
err=DCIHandleFree((dvoid*)pEnv,(ub4)OCI_HTYPE_ENV);
if(err!=OCI_SUCCESS)
{
printf(“OCIHandleFreeOCI_HTYPE_ENVfailed\n”);
}
pEnv=NULL;