金仓数据库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;