Oracle调用接口(OCI)源码剖析(1):创建数据库连接
概述 具体而言,OCI的C语言API包括了两个文件:db_ora_oci_ux.h和db_ora_oci_ux.c。db_ora_oci_ux.h是头文件,而所有与数据库的交互操作的实现都是在db_ora_oci_ux.c中完成的。 本文对OCI的创建数据库连接操作的源码进行简单的剖析。 OCI中建立数据库连接的源码剖析 void *CDbCreateDb(INT8 *pDbType,INT8*pServer,INT8 *pDbName,INT8 *pUser,INT8 *pPwd)
{
CDbRecordset *pcolbuf = NULL;
OCIHDBC hdbc = NULL;
CDb *hDb = NULL;
if (NULL == pServer)
{
WriteLog("CDbCreateDb: CDbCreateDb[0] failed",NULL,NULL);
return NULL;
}
/* 申请句柄指针空间 */
hDb = (CDb *)OsGetUB(sizeof(CDb));
if (NULL == hDb)
{
WriteLog("CDbCreateDb: CDbCreateDb[1] failed",NULL);
return NULL;
}
hDb->hdbc = NULL;
hDb->hRec = NULL;
pthread_mutex_lock(&s_dbmutex);
/* 初始化数据库连接 */
hdbc = DoDbInit((text *)pServer,(text *)pUser,(text *)pPwd);
if (NULL == hdbc)
{
pthread_mutex_unlock(&s_dbmutex);
OsRetUB((UINT8*)hDb);
return NULL;
}
/* 创建结果集 */
pcolbuf = DoRecInit();
if (NULL == pcolbuf)
{
pthread_mutex_unlock(&s_dbmutex);
OsRetUB((UINT8*)hDb);
DoDbFree(hdbc);
return NULL;
}
hDb->hdbc = hdbc;
hDb->hRec = pcolbuf;
hDb->iDbType = CDB_TYPE_ORACLE;
pthread_mutex_unlock(&s_dbmutex);
returnhDb;
}
从该函数的代码实现中,我们可以看到: 2)申请句柄指针空间操作是由OsGetUB函数实现的,初始化数据库连接操作是由DoDbInit函数实现的,创建结果集操作是由DoRecInit函数实现的。 3)为了防止在多个流程中同时调用该函数,在初始化数据库连接之前采用了加锁操作,这保证了每一个创建数据库的操作所返回的句柄是唯一的。 4)如果初始化数据库连接操作函数DoDbInit执行失败了,程序就会执行OsRetUB函数来释放句柄指针空间(该操作与之前的申请句柄指针空间操作对应起来)。 5)如果创建结果集操作函数DoRecInit执行失败了,程序除了执行OsRetUB函数来释放句柄指针空间之外,还会执行DoDbFree函数来释放数据库连接(该操作与之前的初始化数据库连接操作对应起来)。 初始化数据库连接操作函数DoDbInit的代码如下: static void *DoDbInit(text *dblink,text*uid,text *pwd)
{
OCIHDBC hdbc = NULL;
sword rc = (sword)0;
char errBuf[200];
sb4 errcode;
/* 申请所有句柄指针保存空间 */
hdbc = (OCIHDBC)OsGetUB(sizeof(t_envctx));
if (NULL == hdbc)
{
WriteLog("DoDbInit: OsGetUB failed",NULL,NULL);
return NULL;
}
/* 创建OCI环境 */
if (OCIInitialize((ub4)OCI_THREADED|OCI_OBJECT,(dvoid *)0,(dvoid * (*)(dvoid *,size_t))0,dvoid*,(void (*)(dvoid *,dvoid*))0))
{
WriteLog("DoDbInit: OCIInitialize fail",NULL);
return NULL;
}
if(OCIEnvInit((OCIEnv **)&hdbc->envhp,(ub4)OCI_DEFAULT,(size_t)0,(dvoid **)0))
{
WriteLog("DoDbInit: OCIEnvInit fail",NULL);
return NULL;
}
/*申请错误句柄 */
if(OCIHandleAlloc((dvoid *)hdbc->envhp,(dvoid **)&hdbc->errhp,(ub4)OCI_HTYPE_ERROR,(dvoid **)0))
{
WriteLog("DoDbInit: OCIHandleAlloc allocate errhp fail",NULL);
return NULL;
}
/* 申请服务器句柄 */
if(OCIHandleAlloc((dvoid *)hdbc->envhp,(dvoid **)&hdbc->srvhp,(ub4)OCI_HTYPE_SERVER,(dvoid **)0))
{
OCIErrorGet((dvoid *)hdbc->errhp,1,&errcode,(text*)errBuf,(ub4)sizeof(errBuf),(ub4)OCI_HTYPE_ERROR);
OCIHandleFree((dvoid *)hdbc->errhp,(ub4) OCI_HTYPE_ERROR);
WriteLog("DoDbInit: OCIHandleAlloc allocate srvhp fail",NULL);
WriteLog(errBuf,NULL);
return NULL;
}
/* 申请服务环境句柄 */
if(OCIHandleAlloc((dvoid *)hdbc->envhp,(dvoid **)&hdbc->svchp,(ub4)OCI_HTYPE_SVCCTX,(dvoid * *)0))
{
OCIErrorGet((dvoid *)hdbc->errhp,(ub4)OCI_HTYPE_ERROR);
OCIHandleFree((dvoid *)hdbc->srvhp,(ub4) OCI_HTYPE_SERVER);
OCIHandleFree((dvoid *)hdbc->errhp,(ub4) OCI_HTYPE_ERROR);
WriteLog("DoDbInit: OCIHandleAlloc allocate svchp fail",NULL);
return NULL;
}
/* 连接数据库 */
if (OCIServerAttach(hdbc->srvhp,hdbc->errhp,dblink,(sb4)strlen((char*)dblink),(ub4)OCI_DEFAULT))
{
/* 释放环境句柄,系统自动释放在其下所分配的所有其它句柄 */
OCIErrorGet((dvoid *)hdbc->errhp,(ub4)OCI_HTYPE_SERVER);
OCIHandleFree((dvoid *)hdbc->svchp,(ub4)OCI_HTYPE_SVCCTX);
OCIHandleFree((dvoid *)hdbc->errhp,(ub4)OCI_HTYPE_ERROR);
OCIHandleFree((dvoid *)hdbc->envhp,(ub4)OCI_HTYPE_ENV);
OsRetUB((UINT8*)hdbc);
WriteLog("DoDbInit: OCIServerAttach fail",NULL);
return NULL;
}
/* 设置服务环境的服务器属性 */
OCIAttrSet((dvoid *)hdbc->svchp,(dvoid *)hdbc->srvhp,(ub4)0,(ub4)OCI_ATTR_SERVER,hdbc->errhp);
/* 申请用户会话句柄 */
OCIHandleAlloc((dvoid *)hdbc->envhp,(dvoid **)&hdbc->authp,(ub4)OCI_HTYPE_SESSION,(dvoid **)0);
/* 设置会话所使用的用户帐户和密码 */
if (OCIAttrSet((dvoid *)hdbc->authp,(dvoid *)uid,(ub4)strlen((char *)uid),(ub4)OCI_ATTR_USERNAME,hdbc->errhp))
{
OCIServerDetach(hdbc->srvhp,(ub4)OCI_DEFAULT);
OCIHandleFree((dvoid *)hdbc->srvhp,(ub4)OCI_HTYPE_ERROR);
OCIHandleFree((dvoid *)hdbc->authp,(ub4)OCI_HTYPE_SESSION);
OCIHandleFree((dvoid *)hdbc->envhp,(ub4)OCI_HTYPE_ENV);
OsRetUB((UINT8*)hdbc);
WriteLog("DoDbInit: OCIAttrSet[OCI_ATTR_USERNAME] fail",NULL);
return NULL;
}
if (OCIAttrSet((dvoid *)hdbc->authp,(dvoid *)pwd,(ub4)strlen((char *)pwd),(ub4)OCI_ATTR_PASSWORD,(ub4)OCI_HTYPE_ENV);
OsRetUB((UINT8*)hdbc);
WriteLog("DoDbInit: OCIAttrSet[OCI_ATTR_PASSWORD] fail",NULL);
return NULL;
}
/* 建立数据库操作会话 */
if ( (rc = OCISessionBegin(hdbc->svchp,hdbc->authp,(ub4)OCI_CRED_RDBMS,(ub4)OCI_DEFAULT)))
{
DoDbErrProc(hdbc->errhp,rc,"OCISessionBegin");
OCIServerDetach(hdbc->srvhp,(ub4)OCI_HTYPE_ENV);
OsRetUB((UINT8*)hdbc);
WriteLog("DoDbInit: OCISessionBegin fail",NULL);
return NULL;
}
/* 设置会话服务环境 */
if (OCIAttrSet((dvoid *)hdbc->svchp,(dvoid *)hdbc->authp,(ub4)OCI_ATTR_SESSION,hdbc->errhp))
{
OCISessionEnd(hdbc->svchp,(ub4)0);
OCIServerDetach(hdbc->srvhp,(ub4)OCI_HTYPE_ENV);
OsRetUB((UINT8*)hdbc);
WriteLog("DoDbInit:OCIAttrSet[OCI_ATTR_SESSION] fail",NULL);
return NULL;
}
hdbc->stmthp = NULL;
return hdbc;
}
下面对DoDbInit函数进行分析: 2)实现以上十一步操作的函数均是OCI底层提供的(都以OCI打头)。不管哪一步操作执行失败,都会输出相关的日志,可供排查问题。 3)所有OCI主要句柄数据结构OCIHDBC的实现如下: /* 所有OCI主要句柄数据结构 */
typedef struct
{
OCIEnv *envhp; /* 环境句柄 */
OCIError *errhp; /* 错误句柄 */
OCIServer *srvhp; /* 服务器句柄 */
OCISvcCtx *svchp; /* 服务环境句柄 */
OCISession *authp; /* 会话句柄 */
OCIStmt *stmthp; /* 语句句柄 */
}t_envctx;
typedef t_envctx *OCIHDBC; /* 方便使用定义OCIHDBC数据类型 */
以上不同的操作是对OCIHDBC结构体中对应的句柄赋值。 创建结果集操作函数DoRecInit的代码如下: static CDbRecordset *DoRecInit()
{
CDbRecordset *hRecordset;
hRecordset = (CDbRecordset *)OsGetUB(sizeof(CDbRecordset));
if (NULL == hRecordset)
{
WriteLog("DbInitRecordset: DbInitRecordset[0] fail",NULL);
return NULL;
}
memset((void *)hRecordset,0,sizeof(CDbRecordset));
return hRecordset;
}
下面对DoRecInit函数进行分析: 2)结果集结构体CDbRecordset的代码如下: typedef struct CDbRecordsetTag
{
void *cmd; /* 命令缓冲区 */
int sqltype; /* 1:select 2:other*/
int colCount; /* 返回列数 */
char colfieldname[CDB_MAX_COL_NUM][40];/* 每列列名 */
int colfieldlength[CDB_MAX_COL_NUM]; /* 列名宽度 */
int pColWidth[CDB_MAX_COL_NUM]; /* 每列宽度 */
int pColType[CDB_MAX_COL_NUM]; /* 列类型 */
char pRecordBuf[CDB_MAX_COL_NUM][CDB_MAX_COL_WIDTH];/* 列数据 */
int pRetColWidth[CDB_MAX_COL_NUM];
short pRetIndicator[CDB_MAX_COL_NUM];
} CDbRecordset;
如果后续操作要从数据库中获取数据,那么这些数据就用CDbRecordset结构体来存储。 释放数据库连接操作函数DoDbFree的代码如下: static void DoDbFree(OCIHDBC hdbc)
{
if (NULL == hdbc)
{
return;
}
OCISessionEnd(hdbc->svchp,hdbc->errhp,hdbc->authp,(ub4)0);
OCIServerDetach(hdbc->srvhp,(ub4)OCI_DEFAULT);
OCIHandleFree((dvoid *)hdbc->srvhp,(ub4)OCI_HTYPE_SERVER);
OCIHandleFree((dvoid *)hdbc->svchp,(ub4)OCI_HTYPE_SVCCTX);
OCIHandleFree((dvoid *)hdbc->errhp,(ub4)OCI_HTYPE_ERROR);
OCIHandleFree((dvoid *)hdbc->authp,(ub4)OCI_HTYPE_SESSION);
OCIHandleFree((dvoid *)hdbc->envhp,(ub4)OCI_HTYPE_ENV);
OsRetUB((UINT8*)hdbc);
hdbc = NULL;
}
从代码可以看出,该函数的功能是依次释放在DoDbInit函数中所申请的句柄指针。所有的以OCI开头的函数都是OCI底层提供的。 创建数据库连接函数CDbCreateDb的调用 INT32 main(void)
{
INT8 szDBServerName[50] = {0};
INT8 szDBName[50] = {0};
INT8 szDBUser[50] = {0};
INT8 szDBPwd[50] = {0};
void *pDBHandle = NULL;
// 获取数据库各参数的值
memcpy(szDBServerName,"db10_10_10_10",strlen("db10_10_10_10"));
memcpy(szDBName,"dbp_166",strlen("dbp_166"));
memcpy(szDBUser,strlen("dbp_166"));
memcpy(szDBPwd,strlen("dbp_166"));
// 连接数据库
pDBHandle = CDbCreateDb("Oracle",szDBServerName,szDBName,szDBUser,szDBPwd);
if (pDBHandle == NULL) // 连接失败
{
printf("ConnectDB failed! ServiceName:%s,DBName:%s,User:%s,Pwd:%s",szDBPwd);
return -1;
}
printf("ConnectDB success! ServiceName:%s,szDBPwd);
return 0;
}
说明: 2)只有在数据库句柄分配成功(也就是数据库连接建立成功)的情况下,程序才能执行后续操作;如果数据库句柄分配失败,要及时找到失败原因。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |