Oracle调用接口(OCI)源码剖析(2):执行SQL语句并获取结果
概述 下面对这两个函数的源码进行分析。 OCI中执行SQL语句并获取结果的源码剖析 1.执行普通SQL语句的操作 INT32 CDbExecSql(void *pHandle,INT8 *pSQL) { CDb *hDb = (CDb *)pHandle; CDbRecordset *pRec = NULL; OCIHDBC hdbc; sword rc = (sword)0; INT32 row_num = 0; if ((NULL == hDb) || (NULL == hDb->hdbc) || (NULL == hDb->hRec)) { return OCI_ERROR; } hdbc = hDb->hdbc; pRec = hDb->hRec; /* 初始化结果集行数据Buf */ memset((void *)pRec,0,sizeof(CDbRecordset)); /* 申请语句句柄 */ rc = OCIHandleAlloc((dvoid *)hdbc->envhp,(dvoid**)&hdbc->stmthp,(ub4)OCI_HTYPE_STMT,(size_t)0,(dvoid **)0); if (RETCODE_IS_FAILURE(rc)) { DoDbErrProc(hdbc->errhp,rc,"OCIHandleAlloc[OCI_HTYPE_STMT]"); return OCI_ERROR; } /* 准备执行数据库操作 */ rc = DoDbSQLExecute(hDb,(text *)pSQL); if (RETCODE_IS_FAILURE(rc)) { /************************************************ * 如果语句是事务,则以下语句可保证不锁表; * * 如果不是事务,也无关大局 * ************************************************/ if (OCI_STMT_SELECT != pRec->sqltype) { OCITransRollback(hdbc->svchp,hdbc->errhp,(ub4)0); } /* 需要释放stmt句柄 */ DoStmtFree(hdbc->stmthp); return OCI_ERROR; } /* 对于非Select操作,直接返回 */ if (OCI_STMT_SELECT != pRec->sqltype) { /* 获取受delete insert update所影响的列数 */ rc = OCIAttrGet((dvoid *)hdbc->stmthp,(dvoid *)&row_num,(ub4 *)0,OCI_ATTR_ROW_COUNT,hdbc->errhp); if (RETCODE_IS_FAILURE(rc)) { row_num = OCI_ERROR; } /* 需要释放stmt句柄 */ DoStmtFree(hdbc->stmthp); if ( row_num == OCI_ERROR ) { return C_OS_FAIL; } return C_OS_SUCCESS; } /* 定义结果集 */ rc = DoDbDefine(hDb); if ((OCI_ERROR == rc) || (0 == rc)) { rc = DoDbErrProc(hdbc->errhp,"DoDbDefine"); /* 需要释放stmt句柄 */ DoStmtFree(hdbc->stmthp); return rc; } return OCI_SUCCESS; } 从该函数的代码实现中,我们可以看到: 2)申请语句句柄操作是由OCIHandleAlloc函数实现的,它是OCI底层自带的函数。 3)执行数据库操作是由DoDbSQLExecute函数实现的,该函数主要执行这几步操作:第一步,执行OCIStmtPrepare函数准备SQL语句;第二步,执行OCIAttrGet函数获取数据库操作类型;第三步,执行OCIStmtExecute函数进行具体的数据库操作。 4)定义结果集操作是由DoDbDefine函数实现的,该函数主要执行这几步操作:第一步,执行OCIAttrGet函数提取数据库操作返回列的列数;第二步,执行OCIParamGet函数在语句句柄中指定参数描述符;第三步,执行OCIAttrGet函数获得返回结果集列数据类型;第四步,执行OCIAttrGet函数获得返回结果集列名称;第五步,执行OCIDefineByPos函数定义输出变量。 2.获取数据库的返回结果的操作 INT32 CDbFetch(void *pHandle,INT8 *pData,INT32 maxlen) { CDb *hDb = (CDb*)pHandle; CDbRecordset *pRec = NULL; OCIHDBC hdbc; sword rc =(sword)0; INT32 row_num = 0,iLoop,pos = 0; if ((NULL == hDb) || (NULL == hDb->hdbc) || (NULL == hDb->hRec)) { return OCI_ERROR; } hdbc = hDb->hdbc; pRec = hDb->hRec; if ((NULL == hdbc->stmthp) || (NULL == hdbc->errhp)) { return OCI_ERROR; } /* 获取结果数据 */ memset((void *)pData,maxlen); memset(pRec->pRecordBuf,CDB_MAX_COL_NUM * CDB_MAX_COL_WIDTH); rc = OCIStmtFetch(hdbc->stmthp,hdbc->errhp,1,OCI_FETCH_NEXT,OCI_DEFAULT); if (OCI_NO_DATA == rc) { DoStmtFree(hdbc->stmthp); return CDB_FETCH_NO_DATA; } rc = DoDbErrProc(hdbc->errhp,"OCIStmtFetch"); if ((OCI_SUCCESS != rc) && (OCI_SUCCESS_WITH_INFO != rc)) { /* 需要释放stmt句柄 */ DoStmtFree(hdbc->stmthp); return OCI_ERROR; } /* 向pData填充结果集数据 */ for (iLoop = 0; iLoop < pRec->colCount; iLoop++) { int len; /* 清除尾部空格 */ len = pRec->pColWidth[iLoop]; switch (pRec->pColType[iLoop]) { case SQLT_AFC: case SQLT_AVC: case SQLT_CHR: case SQLT_STR: len = DoDbrTrim(pRec->pRecordBuf[iLoop],sizeof(pRec->pRecordBuf[iLoop])); break; default: break; } /* 注意:如果长度不够,返回值也是成功的 */ if (pos + pRec->pColWidth[iLoop] > maxlen) { break; } /* 拷贝实际长度数据 */ memcpy((void *)(pData + pos),(const void *)pRec->pRecordBuf[iLoop],len); /* 偏转定义长度 */ pos += pRec->pColWidth[iLoop]; } return OCI_SUCCESS; } 从该函数的代码实现中,我们可以看到: 2)在获取结果数据之前,先用OCIStmtFetch函数来提取固定数目的记录,同时也可判断数据库中是否有结果数据返回,如果没有,则不执行后续操作。 3)注意函数中的for循环,它将数据库返回的结果按照列的顺序依次拷贝到输出缓存中。其中,数据库返回数据的列数是pRec->colCount,每列宽度存放在pRec->pColWidth[CDB_MAX_COL_NUM]数组中,每列的具体内容存放在pRec->pRecordBuf[CDB_MAX_COL_NUM][CDB_MAX_COL_WIDTH]二维数组中。 执行SQL语句并获取结果的CDbExecSql和CDbFetch函数调用 CDbExecSql和CDbFetch函数调用要在获取了数据库的连接之后,也就是说,要连上数据库之后才能获取结果。 CDbExecSql和CDbFetch函数调用的示例代码如下: INT32 main(void) { INT8 szDBServerName[50] = {0}; INT8 szDBName[50] = {0}; INT8 szDBUser[50] = {0}; INT8 szDBPwd[50] = {0}; INT8 szSqlBuf[100] = {0}; INT8 szRcvBuf[100] = {0}; INT32 iRetVal = 0; void *pDBHandle = NULL; // 获取数据库各参数的值 memcpy(szDBServerName,"db192_1_8_13",strlen("db192_1_8_13")); 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:%sn",szDBPwd); return -1; } printf("ConnectDB success! ServiceName:%s,szDBPwd); // 执行SQL语句并获取结果 // 获取SQL语句 memcpy(szSqlBuf,"select boxnumber from tb_test where id=1",strlen("select boxnumber from tb_test where id=1")); // 调用CDbExecSql函数执行SQL语句 iRetVal = CDbExecSql(pDBHandle,szSqlBuf); if (iRetVal != 0) // 执行失败 { printf("CDbExecSql failed! RetVal=%d (ServiceName:%s,Pwd:%s)n",iRetVal,szDBPwd); return -1; } // 调用CDbFetch函数获取数据库返回的结果 iRetVal = CDbFetch(pDBHandle,szRcvBuf,100); if (iRetVal != 0) // 执行失败 { printf("CDbFetch failed! RetVal=%d (ServiceName:%s,szDBPwd); return -1; } // 打印从数据库中获取到的结果 printf("RcvBuf=%sn",szRcvBuf); return 0; } 说明: 2)CDbFetch函数必须要在CDbExecSql函数执行成功之后才能执行,因为前者要靠后者从数据库中获取结果。 (编辑:4S站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |