金仓数据库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)
goto end;
/* Allocate Server Handle */
err = DCIHandleAlloc(envhp, (dvoid **) &((*svchp)->pServer), DCI_HTYPE_SERVER, (size_t) 0, NULL);
if (err != DCI_SUCCESS)
goto end;
/* Set Server Into ServerContext */
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);
/* Allocate Session Handle */
err = DCIHandleAlloc(envhp, (dvoid **) &((*svchp)->pSession), (ub4) DCI_HTYPE_SESSION, (size_t) 0, NULL);
if (err != DCI_SUCCESS)
goto end;
/* Set Session Into ServerContext */
err = DCIAttrSet(*svchp, DCI_HTYPE_SVCCTX, (*svchp)->pSession, 0, DCI_ATTR_SESSION, errhp);
/* Set Session UserName */
err = DCIAttrSet((*svchp)->pSession, DCI_HTYPE_SESSION, (void *) username, (ub4)strlen(username), DCI_ATTR_USERNAME, errhp);
/* Set Session Password */
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;
}
#ifdef NCI_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, "A transaction is currently being executed.", 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("OCIHandleFree OCI_HTYPE_SVCCTX failed\n");
}
err = DCIHandleFree((dvoid *) pError, (ub4) OCI_HTYPE_ERROR);
if (err != OCI_SUCCESS)
{
printf("OCIHandleFree OCI_HTYPE_ERROR failed\n");
}
pError = NULL;
err = DCIHandleFree((dvoid *) pEnv, (ub4) OCI_HTYPE_ENV);
if (err != OCI_SUCCESS)
{
printf("OCIHandleFree OCI_HTYPE_ENV failed\n");
}
pEnv = NULL;




