Oracle调用接口(OCI)源码剖析(2):执行SQL语句并获取结果

简介: 概述 接着上一篇文章《Oracle调用接口(OCI)源码剖析(1):创建数据库连接》,我们继续对OCI中执行SQL语句并获取结果的源码进行剖析。

概述
接着上一篇文章《Oracle调用接口(OCI)源码剖析(1):创建数据库连接》,我们继续对OCI中执行SQL语句并获取结果的源码进行剖析。该操作主要是由两个函数完成的:CDbExecSql和CDbFetch,其中CDbExecSql函数用于执行普通SQL语句,CDbFetch函数用于获取数据库的返回结果。

下面对这两个函数的源码进行分析。

OCI中执行SQL语句并获取结果的源码剖析

1.执行普通SQL语句的操作
在OCI中,执行普通SQL语句的操作是由CDbExecSql函数实现的,其代码如下:

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, (ub4)OCI_HTYPE_STMT
                      , (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, rc, "DoDbDefine");
       /* 需要释放stmt句柄 */
       DoStmtFree(hdbc->stmthp);
       return rc;
   }
   return OCI_SUCCESS;
}

从该函数的代码实现中,我们可以看到:
1)执行普通SQL语句包括这几步主要操作:第一步,申请语句句柄;第二步,执行数据库操作;第三步,对于非Select操作直接返回,对于Select操作定义结果集。

2)申请语句句柄操作是由OCIHandleAlloc函数实现的,它是OCI底层自带的函数。

3)执行数据库操作是由DoDbSQLExecute函数实现的,该函数主要执行这几步操作:第一步,执行OCIStmtPrepare函数准备SQL语句;第二步,执行OCIAttrGet函数获取数据库操作类型;第三步,执行OCIStmtExecute函数进行具体的数据库操作。

4)定义结果集操作是由DoDbDefine函数实现的,该函数主要执行这几步操作:第一步,执行OCIAttrGet函数提取数据库操作返回列的列数;第二步,执行OCIParamGet函数在语句句柄中指定参数描述符;第三步,执行OCIAttrGet函数获得返回结果集列数据类型;第四步,执行OCIAttrGet函数获得返回结果集列名称;第五步,执行OCIDefineByPos函数定义输出变量。

2.获取数据库的返回结果的操作
在OCI中,获取数据库的返回结果的操作是由CDbFetch函数实现的,其代码如下:

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, 0, maxlen);
   memset(pRec->pRecordBuf, 0, 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, rc, "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;
}

从该函数的代码实现中,我们可以看到:
1)该函数主要完成将CDbExecSql函数执行SQL语句之后的结果拷贝到输出缓存(pData)的操作。

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,      "dbp_166",     strlen("dbp_166"));
   memcpy(szDBPwd,       "dbp_166",     strlen("dbp_166"));

   // 连接数据库
   pDBHandle = CDbCreateDb("Oracle", szDBServerName, szDBName,szDBUser, szDBPwd);

   if (pDBHandle == NULL)    // 连接失败
   {
       printf("ConnectDB failed! ServiceName:%s, DBName:%s, User:%s,Pwd:%s\n", szDBServerName, szDBName, szDBUser, szDBPwd);

       return -1;
   }

   printf("ConnectDB success! ServiceName:%s, DBName:%s, User:%s,Pwd:%s\n", szDBServerName, szDBName, szDBUser, 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, DBName:%s,User:%s, Pwd:%s)\n", iRetVal, szDBServerName, szDBName, szDBUser,szDBPwd);

       return -1;
   }

   // 调用CDbFetch函数获取数据库返回的结果
   iRetVal = CDbFetch(pDBHandle, szRcvBuf, 100);
   if (iRetVal != 0)    // 执行失败
   {
       printf("CDbFetch failed! RetVal=%d (ServiceName:%s, DBName:%s,User:%s, Pwd:%s)\n", iRetVal, szDBServerName, szDBName, szDBUser,szDBPwd);

        return -1;
   }

   // 打印从数据库中获取到的结果
   printf("RcvBuf=%s\n", szRcvBuf);

   return 0;
}

说明:
1)CDbExecSql函数的两个输入参数分别是:数据库句柄和SQL语句缓存,CDbFetch函数的三个输入参数分别是:数据库句柄、结果集缓存及缓存大小。其中,数据库句柄是由CDbCreateDb函数执行之后获得的。

2)CDbFetch函数必须要在CDbExecSql函数执行成功之后才能执行,因为前者要靠后者从数据库中获取结果。

目录
相关文章
|
4天前
|
SQL 存储 Oracle
Oracle的PL/SQL定义变量和常量:数据的稳定与灵动
【4月更文挑战第19天】在Oracle PL/SQL中,变量和常量扮演着数据存储的关键角色。变量是可变的“魔术盒”,用于存储程序运行时的动态数据,通过`DECLARE`定义,可在循环和条件判断中体现其灵活性。常量则是不可变的“固定牌”,一旦设定值便保持不变,用`CONSTANT`声明,提供程序稳定性和易维护性。通过 `%TYPE`、`NOT NULL`等特性,可以更高效地管理和控制变量与常量,提升代码质量。善用两者,能优化PL/SQL程序的结构和性能。
|
3月前
|
SQL Oracle 关系型数据库
整合Mybatis-Plus高级,Oracle 主键Sequence,Sql 注入器实现自定义全局操作
整合Mybatis-Plus高级,Oracle 主键Sequence,Sql 注入器实现自定义全局操作
87 0
|
4天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL游标属性:数据的“导航仪”与“仪表盘”
【4月更文挑战第19天】Oracle PL/SQL游标属性如同车辆的导航仪和仪表盘,提供丰富信息和控制。 `%FOUND`和`%NOTFOUND`指示数据读取状态,`%ROWCOUNT`记录处理行数,`%ISOPEN`显示游标状态。还有`%BULK_ROWCOUNT`和`%BULK_EXCEPTIONS`增强处理灵活性。通过实例展示了如何在数据处理中利用这些属性监控和控制流程,提高效率和准确性。掌握游标属性是提升数据处理能力的关键。
|
4天前
|
SQL Oracle 安全
Oracle的PL/SQL循环语句:数据的“旋转木马”与“无限之旅”
【4月更文挑战第19天】Oracle PL/SQL中的循环语句(LOOP、EXIT WHEN、FOR、WHILE)是处理数据的关键工具,用于批量操作、报表生成和复杂业务逻辑。LOOP提供无限循环,可通过EXIT WHEN设定退出条件;FOR循环适用于固定次数迭代,WHILE循环基于条件判断执行。有效使用循环能提高效率,但需注意避免无限循环和优化大数据处理性能。掌握循环语句,将使数据处理更加高效和便捷。
|
4天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL条件控制:数据的“红绿灯”与“分岔路”
【4月更文挑战第19天】在Oracle PL/SQL中,IF语句与CASE语句扮演着数据流程控制的关键角色。IF语句如红绿灯,依据条件决定程序执行路径;ELSE和ELSIF提供多分支逻辑。CASE语句则是分岔路,按表达式值选择执行路径。这些条件控制语句在数据验证、错误处理和业务逻辑中不可或缺,通过巧妙运用能实现高效程序逻辑,保障数据正确流转,支持企业业务发展。理解并熟练掌握这些语句的使用是成为合格数据管理员的重要一环。
|
4天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL表达式:数据的魔法公式
【4月更文挑战第19天】探索Oracle PL/SQL表达式,体验数据的魔法公式。表达式结合常量、变量、运算符和函数,用于数据运算与转换。算术运算符处理数值计算,比较运算符执行数据比较,内置函数如TO_CHAR、ROUND和SUBSTR提供多样化操作。条件表达式如CASE和NULLIF实现灵活逻辑判断。广泛应用于SQL查询和PL/SQL程序,助你驾驭数据,揭示其背后的规律与秘密,成为数据魔法师。
|
1月前
|
SQL Oracle 关系型数据库
Oracle系列十一:PL/SQL
Oracle系列十一:PL/SQL
|
1月前
|
SQL Oracle 关系型数据库
Oracle系列之八:SQL查询
Oracle系列之八:SQL查询
|
3月前
|
SQL 存储 Oracle
oracle如何定期备份数据库sql文件
【1月更文挑战第7天】oracle如何定期备份数据库sql文件
58 8
|
3月前
|
SQL Oracle 关系型数据库
Oracle PL/SQL基础知识及应用案例
Oracle PL/SQL基础知识及应用案例
33 0

推荐镜像

更多