OCI : ORA-01405: 提取的列值为 NULL

前言

4个月以前,预研了tns314的包分析。当时写了一个OCI测试程序,用来配合抓包。
现在将运行环境搞掉了,重新安装客户端后,发生了2个问题。
* 装32bits的oraclient, 编译的是64bits的OCI测试程序,引起报错。
解决方法:将测试工程种加入32bits和64bits的OCI库,根据安装的oraclient的不同(32bits/64bits), 也编译成(32bits/64bits)进行测试。
* 遍历行集时,出现报错: ORA-01405: 提取的列值为 NULL, 导致测试程序无法遍历结果集。
解决方法:遍历行集时,如果错误码不是OCI_NO_DATA,说明行集没遍历完,可以继续遍历下一行。只是这一行有NULL内容的字段(用sqldeveloper.exe看了对应的表,确实有NULL内容的字段).

花时间做过的实验,隔的时间长了,再回头来弄,都忘的差不多了。
如果及时做笔记,完善保存测试工程。捡起来要方便些。
特别是自己有多个版本的测试工程时,时间长了,还真要花时间确认一下,才能知道哪个工程才是自己最后完善的那个测试工程。

OCI测试工程下载点

oci_test_on_MP.7z

测试工程预览

// @file oci_test_on_vs2017.cpp
// @brief test oci interface

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <conio.h>

// OCI开发环境配置
// 远端oracle服务器端装好,配好TNS服务名称
//
// 安装oracle客户端软件时,选第3项(开发工具). 
// oracle客户端安装完后,用自带的网络助手配置监听器和远端的TNS服务名称
//
// 将工程设定为win64, 包含进OCI头文件和库文件.
// 在打开远端oracle服务器(进入服务器桌面)的情况下,就可以运行调试测试程序了.
// 不用配ODBS的DSN.

#include "oci.h"
// oci.lib is win64, win32编译不过.
// 要安装win32版的oracle client后,拷贝oci开发目录出来用
#pragma comment(lib, "oci.lib")
// #pragma comment(lib, "ociw32.lib")

// 用oracle网络助手配置的远端网络服务名称, 不是ODBC助手配置出来的dsn名称
#define REMOTE_TNS_SERVICE_NAME "orcl" // 服务名称不区分大小写
#define DB_USER_NAME "system"
#define DB_USER_PWD "system"

#define CMD_QUIT "q"
#define CMD_TEST_SELECT_ALL_BUILD_IN_TABLE "test_select"
#define CMD_TEST_SQL "test_sql"

const char* psz_ary_case_sql[] = {
    // "select table_name,tablespace_name,temporary from user_tables",
    // "select 'x' from dual",
    // "select * from v$version",
    // "select * from REPCAT$_RESOLUTION_METHOD",

    "select * from AQ$_INTERNET_AGENT_PRIVS",

    NULL
};

const char* psz_ary_orcl_all_tbl_name[] = {
    "LOGMNR_PARAMETER$",
    "LOGMNR_SESSION$",
    "MVIEW$_ADV_WORKLOAD",
    "MVIEW$_ADV_BASETABLE",
    "MVIEW$_ADV_SQLDEPEND",
    "MVIEW$_ADV_PRETTY",
    "MVIEW$_ADV_TEMP",
    "MVIEW$_ADV_FILTER",
    "MVIEW$_ADV_LOG",
    "MVIEW$_ADV_FILTERINSTANCE",
    "MVIEW$_ADV_LEVEL",
    "MVIEW$_ADV_ROLLUP",
    "MVIEW$_ADV_AJG",
    "MVIEW$_ADV_FJG",
    "MVIEW$_ADV_GC",
    "MVIEW$_ADV_CLIQUE",
    "MVIEW$_ADV_ELIGIBLE",
    "MVIEW$_ADV_OUTPUT",
    "MVIEW$_ADV_EXCEPTIONS",
    "MVIEW$_ADV_PARAMETERS",
    "MVIEW$_ADV_INFO",
    "MVIEW$_ADV_JOURNAL",
    "MVIEW$_ADV_PLAN",
    "AQ$_QUEUE_TABLES",
    "AQ$_QUEUES", // 这个表特别的大
    "AQ$_SCHEDULES",
    "AQ$_INTERNET_AGENTS",
    "AQ$_INTERNET_AGENT_PRIVS",
    "DEF$_ERROR",
    "DEF$_DESTINATION",
    "DEF$_CALLDEST",
    "DEF$_DEFAULTDEST",
    "DEF$_LOB", // 这个表特别的大
    "DEF$_PROPAGATOR",
    "DEF$_ORIGIN",
    "DEF$_PUSHED_TRANSACTIONS",
    "REPCAT$_REPCAT",
    "REPCAT$_FLAVORS",
    "REPCAT$_REPSCHEMA",
    "REPCAT$_SNAPGROUP",
    "REPCAT$_REPOBJECT",
    "REPCAT$_REPCOLUMN",
    "REPCAT$_KEY_COLUMNS",
    "REPCAT$_GENERATED",
    "REPCAT$_REPPROP",
    "REPCAT$_REPCATLOG",
    "REPCAT$_DDL",
    "REPCAT$_REPGROUP_PRIVS",
    "REPCAT$_PRIORITY_GROUP",
    "REPCAT$_PRIORITY",
    "REPCAT$_COLUMN_GROUP",
    "REPCAT$_GROUPED_COLUMN",
    "REPCAT$_CONFLICT",
    "REPCAT$_RESOLUTION_METHOD",
    "REPCAT$_RESOLUTION",
    "REPCAT$_RESOLUTION_STATISTICS",
    "REPCAT$_RESOL_STATS_CONTROL",
    "REPCAT$_PARAMETER_COLUMN",
    "REPCAT$_AUDIT_ATTRIBUTE",
    "REPCAT$_AUDIT_COLUMN",
    "REPCAT$_FLAVOR_OBJECTS",
    "REPCAT$_TEMPLATE_STATUS",
    "REPCAT$_TEMPLATE_TYPES",
    "REPCAT$_REFRESH_TEMPLATES",
    "REPCAT$_USER_AUTHORIZATIONS",
    "REPCAT$_OBJECT_TYPES",
    "REPCAT$_TEMPLATE_REFGROUPS",
    "REPCAT$_TEMPLATE_OBJECTS",
    "REPCAT$_TEMPLATE_PARMS",
    "REPCAT$_OBJECT_PARMS",
    "REPCAT$_USER_PARM_VALUES",
    "REPCAT$_TEMPLATE_SITES",
    "REPCAT$_SITE_OBJECTS",
    "REPCAT$_RUNTIME_PARMS",
    "REPCAT$_TEMPLATE_TARGETS",
    "REPCAT$_EXCEPTIONS",
    "REPCAT$_INSTANTIATION_DDL",
    "REPCAT$_EXTENSION",
    "REPCAT$_SITES_NEW",
    "LOGSTDBY$PARAMETERS",
    "LOGSTDBY$EVENTS",
    "LOGSTDBY$APPLY_MILESTONE",
    "LOGSTDBY$SCN",
    "LOGSTDBY$FLASHBACK_SCN",
    "LOGSTDBY$PLSQL",
    "LOGSTDBY$SKIP_TRANSACTION",
    "LOGSTDBY$SKIP",
    "LOGSTDBY$SKIP_SUPPORT",
    "LOGSTDBY$HISTORY",
    "LOGSTDBY$EDS_TABLES",
    "DEF$_AQCALL", // 这个表特别的大
    "DEF$_AQERROR", // 这个表特别的大
    "SQLPLUS_PRODUCT_PROFILE",
    "MVIEW$_ADV_INDEX",
    "MVIEW$_ADV_PARTITION",
    "HELP",
    "LOGMNR_GT_TAB_INCLUDE$",
    "LOGMNR_GT_USER_INCLUDE$",
    "LOGMNR_GT_XID_INCLUDE$",
    "LOGMNRT_MDDL$",
    "OL$",
    "OL$HINTS",
    "OL$NODES",
    "LOGMNR_DICTSTATE$",
    "LOGMNRC_GTLO",
    "LOGMNRC_GTCS",
    "LOGMNRC_GSII",
    "LOGMNRC_GSBA",
    "LOGMNR_SEED$",
    "LOGMNR_DICTIONARY$",
    "LOGMNR_OBJ$",
    "LOGMNR_TAB$",
    "LOGMNR_COL$",
    "LOGMNR_ATTRCOL$",
    "LOGMNR_TS$",
    "LOGMNR_IND$",
    "LOGMNR_USER$",
    "LOGMNR_TABPART$",
    "LOGMNR_TABSUBPART$",
    "LOGMNR_TABCOMPART$",
    "LOGMNR_TYPE$",
    "LOGMNR_COLTYPE$",
    "LOGMNR_ATTRIBUTE$",
    "LOGMNR_LOB$",
    "LOGMNR_CDEF$",
    "LOGMNR_CCOL$",
    "LOGMNR_ICOL$",
    "LOGMNR_LOBFRAG$",
    "LOGMNR_INDPART$",
    "LOGMNR_INDSUBPART$",
    "LOGMNR_INDCOMPART$",
    "LOGMNR_LOGMNR_BUILDLOG",
    "LOGMNR_NTAB$",
    "LOGMNR_OPQTYPE$",
    "LOGMNR_SUBCOLTYPE$",
    "LOGMNR_KOPM$",
    "LOGMNR_PROPS$",
    "LOGMNR_ENC$",
    "LOGMNR_REFCON$",
    "LOGMNR_PARTOBJ$",
    "LOGMNRP_CTAS_PART_MAP",
    "LOGSTDBY$APPLY_PROGRESS",
    "MVIEW$_ADV_OWB",
    "LOGMNRC_DBNAME_UID_MAP",
    "LOGMNR_RESTART_CKPT$", // 这个表特别的大
    "LOGMNR_AGE_SPILL$", // 这个表特别的大
    "LOGMNR_SESSION_EVOLVE$",
    "LOGMNR_UID$",
    "LOGMNR_SESSION_ACTIONS$",
    "LOGMNR_SPILL$", // 这个表特别的大
    "LOGMNR_FILTER$",
    "LOGMNR_PROCESSED_LOG$",
    "LOGMNR_GLOBAL$",
    "LOGMNR_RESTART_CKPT_TXINFO$", // 这个表特别的大
    "LOGMNR_LOG$",
    "LOGMNR_ERROR$",
    NULL
};

typedef struct _tag_col_info{
    char* m_psz_name;
    int m_i_len_name;

    int m_i_data_type_org;
    int m_i_data_type_now;
    char* m_psz_data;
    int m_i_len_data;

    _tag_col_info() {
        m_psz_name = NULL;
        m_i_len_name = 0;

        m_i_data_type_org = SQLT_CHR;
        m_i_data_type_now = SQLT_CHR;
        m_psz_data = NULL;
        m_i_len_data = 0;
    }

    void clear() {
        if (NULL != m_psz_name) {
            delete [] m_psz_name;
            m_psz_name = NULL;
        }

        if (NULL != m_psz_data) {
            delete [] m_psz_data;
            m_psz_data = NULL;
        }

        m_i_len_name = 0;
        m_i_len_data = 0;
        m_i_data_type_org = SQLT_CHR;
        m_i_data_type_now = SQLT_CHR;
    }
}TAG_COL_INFO;

void task_readme();
std::string get_oci_error(const char* psz_tip, OCIError* h_oci_error);
void print_oci_error(const char* psz_tip, OCIError* h_oci_error);
void do_oci_task_ex(OCIEnv* h_oci_env, OCIServer* h_oci_server, OCIError* h_oci_error, OCISvcCtx* h_oci_context, OCISession* h_oci_user);
void do_oci_task(const char* psz_sql, OCIEnv* h_oci_env, OCIServer* h_oci_server, OCIError* h_oci_error, OCISvcCtx* h_oci_context, OCISession* h_oci_user);

void case_oci_opt();

int g_i_columns_cnt = 0;
TAG_COL_INFO g_col_info[100];

int _tmain(int argc, _TCHAR* argv[])
{
    case_oci_opt();

    printf("END\n");
    system("pause");
    return 0;
}

void case_oci_opt()
{
    sword sw_rc = 0;
    sword sw_tmp = 0;
    OCIEnv* h_oci_env = NULL; // 环境句柄
    OCIServer* h_oci_server = NULL; // 服务器句柄
    OCIError* h_oci_error = NULL; // 错误句柄
    OCISvcCtx* h_oci_context = NULL; // 上下文句柄
    OCISession* h_oci_user = NULL; // 用户句柄

    sb4 errcode = 0;
    do {
        // 打印数据库连接信息
        printf("connect local tns service name [%s], user name = [%s], password = [%s]\r\n",
            REMOTE_TNS_SERVICE_NAME,
            DB_USER_NAME,
            DB_USER_PWD);

        // 创建环境句柄(线程和环境对象)
        sw_rc = OCIEnvCreate(&h_oci_env, OCI_THREADED | OCI_OBJECT, (dvoid*)0, 0, 0, 0, (size_t)0, (dvoid**)0);
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : create env handle", h_oci_error);
            break;
        }

        // 创建服务器句柄
        sw_rc = OCIHandleAlloc((dvoid*)h_oci_env, (dvoid**)&h_oci_server, OCI_HTYPE_SERVER, 0, (dvoid**)0);
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : create service handle", h_oci_error);
            break;
        }

        // 创建错误句柄
        sw_rc = OCIHandleAlloc((dvoid*)h_oci_env, (dvoid**)&h_oci_error, OCI_HTYPE_ERROR, 0, (dvoid**)0);
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : create error handle", h_oci_error);
            break;
        }

        // OCI连接的不是ODBC DSN名称
        // my_oracle_dsn
        // ORA-12154: TNS: 无法解析指定的连接标识符

        // OCI连接的是远端TNS服务名称
        // TNSPING ORCL
        // 成功

        // 如果远端的oracle服务器关了,这里会显示错误:连接超时
        // ORA-12170: TNS: 连接超时

        // 连接远程数据库(tns服务名称)
        sw_rc = OCIServerAttach(h_oci_server, h_oci_error,
            (text*)REMOTE_TNS_SERVICE_NAME,
            (sb4)strlen(REMOTE_TNS_SERVICE_NAME),
            OCI_DEFAULT);
        // packet 1~18

        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : remote database connect", h_oci_error);
            break;
        }

        // 创建上下文
        sw_rc = OCIHandleAlloc((dvoid*)h_oci_env, (dvoid**)&h_oci_context, OCI_HTYPE_SVCCTX, 0, (dvoid**)0);
        // packet 19~20
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : create context", h_oci_error);
            break;
        }

        // 设置上下文属性
        sw_rc = OCIAttrSet((dvoid**)h_oci_context, OCI_HTYPE_SVCCTX, (dvoid*)h_oci_server, (ub4)0, OCI_ATTR_SERVER, h_oci_error);
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : set context", h_oci_error);
            break;
        }

        // 创建用户句柄
        sw_rc = OCIHandleAlloc((dvoid*)h_oci_env, (dvoid**)&h_oci_user, OCI_HTYPE_SESSION, 0, (dvoid**)0);
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : create user", h_oci_error);
            break;
        }

        // 设置用户名
        sw_rc = OCIAttrSet((dvoid*)h_oci_user, OCI_HTYPE_SESSION, (dvoid*)DB_USER_NAME, (ub4)strlen(DB_USER_NAME), OCI_ATTR_USERNAME, h_oci_error);
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : set user name", h_oci_error);
            break;
        }

        // 设置口令
        sw_rc = OCIAttrSet((dvoid*)h_oci_user, OCI_HTYPE_SESSION, (dvoid*)DB_USER_PWD, (ub4)strlen(DB_USER_PWD), OCI_ATTR_PASSWORD, h_oci_error);
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : set user password", h_oci_error);
            break;
        }

        // 会话开始
        sw_rc = OCISessionBegin(h_oci_context, h_oci_error, h_oci_user, OCI_CRED_RDBMS, OCI_DEFAULT);
        // packet 21~26
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : session begin", h_oci_error);
            break;
        }

        // 在会话上设置用户信息
        sw_rc = OCIAttrSet((dvoid*)h_oci_context, OCI_HTYPE_SVCCTX, (dvoid*)h_oci_user, (ub4)0, OCI_ATTR_SESSION, h_oci_error);
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : set user info on service handle", h_oci_error);
            break;
        }

        // 干活
        do_oci_task_ex(h_oci_env, h_oci_server, h_oci_error, h_oci_context, h_oci_user);
        printf("ok : db operation over\r\n");
    } while (0);

    // 会话结束
    sw_rc = OCISessionEnd(h_oci_context, h_oci_error, h_oci_user, OCI_DEFAULT);
    // packet 39~41
    if (OCI_SUCCESS != sw_rc) {
        print_oci_error("error : end session", h_oci_error);
    }

    // 断开连接
    sw_rc = OCIServerDetach(h_oci_server, h_oci_error, OCI_DEFAULT);
    // packet 42`46
    if (OCI_SUCCESS != sw_rc) {
        print_oci_error("error : detach server", h_oci_error);
    }

    // --------------------------------------------------------------------------------
    // 释放句柄
    // --------------------------------------------------------------------------------
    sw_rc = OCIHandleFree((void*)h_oci_user, OCI_HTYPE_SESSION);
    if (OCI_SUCCESS != sw_rc) {
        print_oci_error("error : OCI_HTYPE_SESSION", h_oci_error);
    }

    sw_rc = OCIHandleFree((void*)h_oci_context, OCI_HTYPE_SVCCTX);
    if (OCI_SUCCESS != sw_rc) {
        print_oci_error("error : OCI_HTYPE_SVCCTX", h_oci_error);
    }

    sw_rc = OCIHandleFree((void*)h_oci_error, OCI_HTYPE_ERROR);
    if (OCI_SUCCESS != sw_rc) {
        print_oci_error("error : OCI_HTYPE_ERROR", NULL);
    }

    sw_rc = OCIHandleFree((void*)h_oci_server, OCI_HTYPE_SERVER);
    if (OCI_SUCCESS != sw_rc) {
        print_oci_error("error : OCI_HTYPE_SERVER", NULL);
    }

    sw_rc = OCIHandleFree((void*)h_oci_env, OCI_HTYPE_ENV);
    if (OCI_SUCCESS != sw_rc) {
        print_oci_error("error : OCI_HTYPE_ENV", NULL);
    }
}

std::string get_oci_error(const char* psz_tip, OCIError* h_oci_error)
{
    char sz_buf[4096] = { '\0' };
    sword sw_rc = 0;
    sb4 errcode = 0;
    std::string str_rc = "";

    if (NULL != psz_tip) {
        str_rc += psz_tip;
    }

    str_rc += "\r\n";

    if (NULL != h_oci_error) {
        sw_rc = OCIErrorGet(
            h_oci_error,
            (ub4)1,
            (text*)NULL,
            &errcode,
            (OraText*)sz_buf,
            (ub4)sizeof(sz_buf),
            (b4)OCI_HTYPE_ERROR);

        if (OCI_SUCCESS == sw_rc) {
            str_rc += "\t";
            str_rc += sz_buf;
        }
    }

    return str_rc;
}

void print_oci_error(const char* psz_tip, OCIError* h_oci_error)
{
    printf("%s\r\n", get_oci_error(psz_tip, h_oci_error).c_str());
}

void do_oci_task_ex(OCIEnv* h_oci_env, OCIServer* h_oci_server, OCIError* h_oci_error, OCISvcCtx* h_oci_context, OCISession* h_oci_user)
{
    char sz_sql[4096] = { '\0' };
    char c_tmp = '\0';
    int i = 0;

    do {
        task_readme();
        fflush(stdin);
        memset(sz_sql, 0, sizeof(sz_sql));

        do {
            c_tmp = _getch();
            if ((c_tmp == '\r') || (strlen(sz_sql) >= (sizeof(sz_sql) - 1))) {
                printf("\n");
                break;
            }

            if ('\b' == c_tmp) {
                task_readme();
                fflush(stdin);
                memset(sz_sql, 0, sizeof(sz_sql));
                continue;
            }

            sz_sql[strlen(sz_sql)] = c_tmp;
            printf("%c", c_tmp);
        } while (1);
        printf("input is : %s\n", sz_sql);
        if (0 == strcmp(CMD_QUIT, sz_sql)) {
            printf("user command to quit\n");
            break;
        }
        else if (0 == strcmp(CMD_TEST_SELECT_ALL_BUILD_IN_TABLE, sz_sql)) {
            for (i = 0; i < sizeof(psz_ary_orcl_all_tbl_name) / sizeof(psz_ary_orcl_all_tbl_name[0]); i++) {
                if (NULL == psz_ary_orcl_all_tbl_name[i]) {
                    break;
                }
                printf("test task [%d] begin ...\n", i);
                memset(sz_sql, 0, sizeof(sz_sql));
                sprintf(sz_sql, "select * from %s", psz_ary_orcl_all_tbl_name[i]);
                printf("execut sql \"%s\" ...\r\n", sz_sql);
                do_oci_task(sz_sql, h_oci_env, h_oci_server, h_oci_error, h_oci_context, h_oci_user);
                printf("test task [%d] end ...\n", i);
            }
        }
        else if (0 == strcmp(CMD_TEST_SQL, sz_sql)) {
            for (i = 0; i < sizeof(psz_ary_case_sql) / sizeof(psz_ary_case_sql[0]); i++) {
                if (NULL == psz_ary_case_sql[i]) {
                    break;
                }
                printf("test task [%d] begin ...\n", i);
                memset(sz_sql, 0, sizeof(sz_sql));
                sprintf(sz_sql, "%s", psz_ary_case_sql[i]);
                printf("execut sql \"%s\" ...\r\n", sz_sql);
                do_oci_task(sz_sql, h_oci_env, h_oci_server, h_oci_error, h_oci_context, h_oci_user);
                printf("test task [%d] end ...\n", i);
            }
        }
        else {
            printf("execut sql \"%s\" ...\r\n", sz_sql);
            do_oci_task(sz_sql, h_oci_env, h_oci_server, h_oci_error, h_oci_context, h_oci_user);
        }
    } while (1);
}

void task_readme()
{
    printf("\n");
    printf("// --------------------------------------------------------------------------------\n");
    printf("// 任务列表:\n");
    printf("// 输入SQL语句, 回车, 执行输入的SQL语句\n");
    printf("// 输入'" CMD_TEST_SELECT_ALL_BUILD_IN_TABLE "', 对预置的表名数组进行select * 操作\n");
    printf("// 输入'" CMD_TEST_SQL "', 执行预置的sql语句数组\n");
    printf("// 输入'" CMD_QUIT "'退出\n");
    printf("// --------------------------------------------------------------------------------\n");
}

void do_oci_task(const char* psz_sql, OCIEnv* h_oci_env, OCIServer* h_oci_server, OCIError* h_oci_error, OCISvcCtx* h_oci_context, OCISession* h_oci_user)
{
    OraText* ora_text_select = (OraText*)psz_sql;

    sword sw_rc = 0;
    OCIStmt* h_oci_stmt = NULL; // SQL语句句柄
    OCIDefine* h_oci_define = NULL;
    OCIParam* h_oci_param = NULL;
    OraText* colName = NULL;
    ub4 colNameSize = 0;

    ub2 stmt_type = 0; // SQL语句类型
    ub4 fieldCount = 0; // 结果集字段数量
    ub4 i_index = 0;
    int i_row_index = 0;

    ub4 col_len = 0;
    ub4 col_lenSize = 0;
    int dtypeNew = 0;

    ub4 dtype = 0;

    do {
        if ((NULL == h_oci_env)
            || (NULL == h_oci_server)
            || (NULL == h_oci_error)
            || (NULL == h_oci_user)
            || (NULL == h_oci_context)) {
            printf("oci handle invalid\r\n");
            break;
        }

        printf("do oci task\r\n");

        if (NULL != psz_sql) {
            SetConsoleTitleA(psz_sql);
        }

        if (0 == strlen(psz_sql)) {
            printf("please input sql\n");
            break;
        }

        // 建立SQL语句句柄
        sw_rc = OCIHandleAlloc(h_oci_env, (void**)&h_oci_stmt, OCI_HTYPE_STMT, (size_t)0, (dvoid**)0);
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : create OCI_HTYPE_STMT", h_oci_error);
            break;
        }

        // 准备SQL语句
        sw_rc = OCIStmtPrepare(h_oci_stmt, h_oci_error, ora_text_select, (ub4)strlen((const char*)ora_text_select), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT);
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : OCIStmtPrepare", h_oci_error);
            break;
        }

        // 执行SQL
        printf("sw_rc = OCIStmtExecute(h_oci_context, h_oci_stmt, h_oci_error, (ub4)0, (ub4)0, (OCISnapshot*)0, (OCISnapshot*)0, OCI_DEFAULT);\r\n");
        sw_rc = OCIStmtExecute(h_oci_context, h_oci_stmt, h_oci_error, (ub4)0, (ub4)0, (OCISnapshot*)0, (OCISnapshot*)0, OCI_DEFAULT);
        // packet 27~29
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : OCIStmtExecute", h_oci_error);
            break;
        }

        // printf("press any key to get result set\n");
        // _getch();

        // 获取SQL语句类型
        sw_rc = OCIAttrGet(h_oci_stmt, OCI_HTYPE_STMT, &stmt_type, NULL, OCI_ATTR_STMT_TYPE, h_oci_error);
        if (OCI_SUCCESS != sw_rc) {
            print_oci_error("error : OCI_ATTR_STMT_TYPE", h_oci_error);
            break;
        }

        // 取结果集列信息
        if (OCI_STMT_SELECT == stmt_type) {
            printf("query result set :\r\n");

            printf("sw_rc = OCIAttrGet(h_oci_stmt, OCI_HTYPE_STMT, &fieldCount, NULL, OCI_ATTR_PARAM_COUNT, h_oci_error);\r\n");
            sw_rc = OCIAttrGet(h_oci_stmt, OCI_HTYPE_STMT, &fieldCount, NULL, OCI_ATTR_PARAM_COUNT, h_oci_error);
            if (OCI_SUCCESS != sw_rc) {
                print_oci_error("error : OCI_ATTR_STMT_TYPE", h_oci_error);
                break;
            }

            printf("query result set columns = %d\r\n", fieldCount);
            g_i_columns_cnt = fieldCount;

            for (i_index = 0; i_index < fieldCount; i_index++) {
                h_oci_param = NULL;
                dtype = 0;

                printf("columns %d : \r\n", i_index + 1);
                ub4 dtypeSize = sizeof(dtype);
                sw_rc = OCIParamGet(h_oci_stmt, OCI_HTYPE_STMT, h_oci_error, (void**)&h_oci_param, i_index + 1);
                if (OCI_SUCCESS != sw_rc)
                {
                    print_oci_error("error : OCIParamGet", h_oci_error);
                    break;
                }

                sw_rc = OCIAttrGet(h_oci_param, OCI_DTYPE_PARAM, &dtype, &dtypeSize, OCI_ATTR_DATA_TYPE, h_oci_error);
                if (OCI_SUCCESS != sw_rc)
                {
                    print_oci_error("error : OCIAttrGet", h_oci_error);
                    break;
                }

                g_col_info[i_index].m_i_data_type_org = (int)dtype;
                printf("\tg_col_info[%d].m_i_data_type_org = %d\r\n", i_index, g_col_info[i_index].m_i_data_type_org);

                colName = NULL;
                colNameSize = sizeof(colName);
                sw_rc = OCIAttrGet(h_oci_param, OCI_DTYPE_PARAM, &colName, &colNameSize, OCI_ATTR_NAME, h_oci_error);
                if (OCI_SUCCESS != sw_rc)
                {
                    print_oci_error("error : OCIAttrGet", h_oci_error);
                    break;
                }

                g_col_info[i_index].m_i_len_name = (int)colNameSize;
                g_col_info[i_index].m_psz_name = new char[g_col_info[i_index].m_i_len_name + 1];
                memset(g_col_info[i_index].m_psz_name, 0, g_col_info[i_index].m_i_len_name + 1);

                // strcpy_s(g_col_info[i_index].m_psz_name, g_col_info[i_index].m_i_len_name, (char*)colName);
                memcpy(g_col_info[i_index].m_psz_name, (char*)colName, g_col_info[i_index].m_i_len_name);

                printf("\tname = %s\r\n", (char*)g_col_info[i_index].m_psz_name);
                printf("\tname len = %d\r\n", (int)g_col_info[i_index].m_i_len_name);

                col_len = 0;
                col_lenSize = sizeof(col_len);
                dtypeNew = SQLT_CHR;
                switch (dtype)
                {
                case SQLT_DAT:
                case SQLT_DATE:
                case SQLT_TIME:
                case SQLT_TIME_TZ:
                case SQLT_TIMESTAMP:
                case SQLT_TIMESTAMP_TZ:
                case SQLT_TIMESTAMP_LTZ:
                {
                    dtypeNew = SQLT_ODT;
                    col_len = sizeof(OCIDate);
                }
                break;
                case SQLT_CLOB:
                case SQLT_CHR:
                case SQLT_INT:
                case SQLT_UIN:
                case SQLT_NUM:
                case SQLT_FLT:
                case SQLT_STR:
                case SQLT_VNU:
                case SQLT_LNG:
                case SQLT_VCS:
                case SQLT_LVC:
                case SQLT_AFC:
                case SQLT_AVC:
                {
                    sw_rc = OCIAttrGet(h_oci_param, OCI_DTYPE_PARAM, &col_len, &col_lenSize, OCI_ATTR_DATA_SIZE, h_oci_error);
                    if (OCI_SUCCESS != sw_rc)
                    {
                        print_oci_error("error : OCIAttrGet", h_oci_error);
                        break;
                    }

                    printf("\tdata len = %d\r\n", (int)col_len);
                    dtypeNew = SQLT_CHR;
                }
                break;
                default:
                    // assert(0);
                    break;
                }

                g_col_info[i_index].m_i_data_type_now = (int)dtypeNew;
                printf("\tg_col_info[%d].m_i_data_type_now = %d\r\n", i_index, g_col_info[i_index].m_i_data_type_now);

                g_col_info[i_index].m_i_len_data = (int)col_len;
                g_col_info[i_index].m_psz_data = new char[g_col_info[i_index].m_i_len_data + 1];
                memset(g_col_info[i_index].m_psz_data, 0, g_col_info[i_index].m_i_len_data + 1);

                h_oci_define = NULL;
                sw_rc = OCIDefineByPos(h_oci_stmt, &h_oci_define, h_oci_error, i_index + 1, g_col_info[i_index].m_psz_data, col_len * sizeof(char), dtypeNew, 0, 0, 0, OCI_DEFAULT);
                if (OCI_SUCCESS != sw_rc)
                {
                    print_oci_error("error : OCIDefineByPos", h_oci_error);
                    break;
                }

                printf("----------------------------------------\r\n");
            }
        }

        // 遍历行集
        i_row_index = 0;
        do {
            sw_rc = OCIStmtFetch(h_oci_stmt, h_oci_error, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
            // 如果是查第一行,并不发收包,可能执行后,返回的数据中已经包含第一行
            // packet 30~32 如果数据不够了,会继续请求包。
            // packet 33~35
            // packet 36~38

            if (OCI_NO_DATA == sw_rc)
            {
                // 只有OCI_NO_DATA代表行集读完了
                printf("OCI_NO_DATA\n");
                break;
            }

            if (OCI_SUCCESS != sw_rc)
            {
                printf("error : sw_rc = %d\n", sw_rc);
                // sw_rc is -1
                print_oci_error("OCIStmtFetch", h_oci_error);
                // ORA-01405: 提取的列值为 NULL

                // 只是这行有问题, 可以继续读下一行
                // 看了表内容, 确实有的字段是NULL
                continue;
            }

            for (i_index = 0; i_index != g_i_columns_cnt; i_index++)
            {
                printf("row[%d]col[%d] = %s\r\n", i_row_index, i_index, (NULL != g_col_info[i_index].m_psz_data) ? g_col_info[i_index].m_psz_data : "NULL");
            }
            printf("----------------------------------------\r\n");
            i_row_index++;
            // break; // @todo for debug, only show one row
        } while (1);

        // 释放资源
        for (i_index = 0; i_index != g_i_columns_cnt; i_index++)
        {
            g_col_info[i_index].clear();
        }
    } while (0);

    printf("press any key to end the query\n");
    _getch();

     printf("will be end query\n");
     Sleep(1000);

    // 释放oci句柄
    if (NULL != h_oci_stmt) {
        sw_rc = OCIHandleFree((void*)h_oci_stmt, OCI_HTYPE_STMT);
    }

     if (OCI_SUCCESS != sw_rc) {
        print_oci_error("error : OCI_HTYPE_STMT", NULL);
    }
}

猜你喜欢

转载自blog.csdn.net/lostspeed/article/details/79992384