OCI : ORA-24333: zero iteration count

前言

在搭建tns310抓包的环境, 服务端是oracle8.1.7.4(从oracle8.1.7.0打了p2376472_8174_WINNT.zip补丁升级来的, 这补丁google了好久…, 不打补丁,用高版本的OCI连上oracle8i有问题,没那么多时间去实验,我信了, 直接上补丁)。

客户端试了几个(oracle9i, oracle10g, oracle11gr2), 在执行select语句之外的SQL, 总会得到ORA-24333的报错, 因为服务端已经定下来了, 没办法换(要不就不是tns310了),一直在试oracle客户端. 一执行select之外的SQL, 就得到ORA-24333报错. 正经活都没开始呢,前置任务就搞了这么久, 真悲伤。

因为测试程序是以前写好的,在服务端和客户端都是oracle11gr2时,都正常的。也不会怀疑是代码写挫了。一直在怀疑高版本oracle_client连接低版本oracle_client,是否有服务端或客户端配置上的问题。

验证失败一会,就开始找资料。再做实验,再失败,再找资料。直到看到csdn上一篇写于2016年9月的博客,才知道代码写错了…。

OCIStmtExecute的参数4是SQL语句的执行次数, 如果是select语句,次数可以为0, 因为后续还可以OCI去拿结果集,不用指定执行次数. 如果是非select语句,值不能为0,至少为1(至少执行一次).

其实问题解决后,再对照报错信息,感觉报错信息离报错点挺近的。
如果OCI编程没搞那么细致,特别是在高版本的OCI环境下实验正常,根本不会怀疑是代码写挫了. 因为执行的SQL语句数组不同,我也不敢肯定是不是原来的代码写对了. 以后有时间再实验,今天收工了。解决这个问题用了3天,杯具。

其实也有环境配置的问题(oracle8.1服务端,oracle客户端9i, 10g)(网络配置问题,用户权限问题,补丁问题). 本来用9i做最初的客户端实验,想着9i离8i不远,可能兼容性更好, 谁想弄出这些事。时间有限,这些曾经出现的问题得等有时间时再验证了.

实验成功的环境配置

服务端: win2k_server_sp4 + oracle8i + p2376472_8174_WINNT.zip. 重新设置了scott用户的权限(加了system权限)
客户端:win10x64 + oracle10.2.0.1.0

修正后的代码片段

        // 准备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) {
            // ora10通过sid连接ora8.1, 执行除了select语句之外的SQL, 都会得到下列错误
            // ORA-24333: 零迭代计数
            print_oci_error("error : OCIStmtPrepare", h_oci_error);
            break;
        }

        // 必须在执行SQL语句前, 获取SQL语句类型用于处理参数4, 防止出现错误 : ORA-24333: zero iteration count
        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;
        }

        // 执行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");
        // 处理参数4, 防止报错; ORA-24333
        sw_rc = OCIStmtExecute(
            h_oci_context, 
            h_oci_stmt, 
            h_oci_error, 
            (ub4)(OCI_STMT_SELECT == stmt_type ? 0 : 1), 
            (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;
        }

oracle官方的API帮助

OCIStmtExecute()
Purpose
This call associates an application request with a server.

Syntax
sword OCIStmtExecute ( OCISvcCtx           *svchp,
                       OCIStmt             *stmtp,
                       OCIError            *errhp,
                       ub4                 iters,
                       ub4                 rowoff,
                       CONST OCISnapshot   *snap_in,
                       OCISnapshot         *snap_out,
                       ub4                 mode );
Parameters
svchp (IN/OUT)
Service context handle.

stmtp (IN/OUT)
An statement handle. It defines the statement and the associated data to be executed at the server. It is invalid to pass in a statement handle that has bind of data types only supported in release 8.x or later when svchp points to an Oracle7 server.

errhp (IN/OUT)
An error handle you can pass to OCIErrorGet() for diagnostic information in the event of an error.

iters (IN)
For non-SELECT statements, the number of times this statement is executed is equal to iters - rowoff.

For SELECT statements, if iters is nonzero, then defines must have been done for the statement handle. The execution fetches iters rows into these predefined buffers and prefetches more rows depending upon the prefetch row count. If you do not know how many rows the SELECT statement will retrieve, set iters to zero.

This function returns an error if iters=0 for non-SELECT statements.

Note:
For array DML operations, set iters <= 32767 to get better performance.

rowoff (IN)
The starting index from which the data in an array bind is relevant for this multiple row execution.

snap_in (IN)
This parameter is optional. if supplied, must point to a snapshot descriptor of type OCI_DTYPE_SNAP. The contents of this descriptor must be obtained from the snap_out parameter of a previous call. The descriptor is ignored if the SQL is not a SELECT. This facility allows multiple service contexts to ORACLE to see the same consistent snapshot of the database's committed data. However, uncommitted data in one context is not visible to another context even using the same snapshot.

snap_out (OUT)
This parameter optional. if supplied, must point to a descriptor of type OCI_DTYPE_SNAP. This descriptor is filled in with an opaque representation which is the current ORACLE "system change number" suitable as a snap_in input to a subsequent call to OCIStmtExecute(). This descriptor should not be used longer than necessary in order to avoid "snapshot too old" errors.

mode (IN)
The modes are:

OCI_BATCH_ERRORS - See "Batch Error Mode for OCIStmtExecute()", for information about this mode.
OCI_COMMIT_ON_SUCCESS - When a statement is executed in this mode, the current transaction is committed after execution, provided that execution completes successfully.
OCI_DEFAULT - Calling OCIStmtExecute() in this mode executes the statement. It also implicitly returns describe information about the select-list.
OCI_DESCRIBE_ONLY - This mode is for users who wish to describe a query prior to execution. Calling OCIStmtExecute() in this mode does not execute the statement, but it does return the select-list description. To maximize performance, it is recommended that applications execute the statement in default mode and use the implicit describe which accompanies the execution.
OCI_EXACT_FETCH - Used when the application knows in advance exactly how many rows it will be fetching. This mode turns prefetching off for Oracle release 8 or later mode, and requires that defines be done before the execute call. Using this mode cancels the cursor after the desired rows are fetched and may result in reduced server-side resource usage.
OCI_PARSE_ONLY - This mode allows the user to parse the query prior to execution. Executing in this mode parses the query and returns parse errors in the SQL, if any. Users must note that this will involve an additional round-trip to the server. To maximize performance, it is recommended that the user execute the statement in the default mode which, as part of a bundled operation, parses the statement.
OCI_STMT_SCROLLABLE_READONLY - Required for the result set to be scrollable. The result set cannot be updated. See "Fetching Results". Cannot be used with any other mode.
The modes are not mutually exclusive and can be used together, except for OCI_STMT_SCROLLABLE_READONLY.

Comments
This function is used to execute a prepared SQL statement. Using an execute call, the application associates a request with a server.

If a SELECT statement is executed, the description of the select-list is available implicitly as a response. This description is buffered on the client side for describes, fetches and define type conversions. Hence it is optimal to describe a select list only after an execute.

See Also:
"Describing Select-List Items"

Also for SELECT statements, some results are available implicitly. Rows will be received and buffered at the end of the execute. For queries with small row count, a prefetch causes memory to be released in the server if the end of fetch is reached, an optimization that may result in memory usage reduction. Set attribute call has been defined to set the number of rows to be prefetched for each result set.

For SELECT statements, at the end of the execute, the statement handle implicitly maintains a reference to the service context on which it is executed. It is the user's responsibility to maintain the integrity of the service context. The implicit reference is maintained until the statement handle is freed or the fetch is cancelled or an end of fetch condition is reached.

Note:
If output variables are defined for a SELECT statement before a call to OCIStmtExecute(), the number of rows specified by iters will be fetched directly into the defined output buffers and additional rows equivalent to the prefetch count will be prefetched. If there are no additional rows, then the fetch is complete without calling OCIStmtFetch().

Related Functions
OCIStmtPrepare()

完整正确的测试程序

开新工程时,将合适的OCI库包进工程就能编译过了.

// @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" // 服务名称不区分大小写

// 可以用scott或system用户, 来执行OCI操作
#define DB_USER_NAME "scott"
#define DB_USER_PWD "tiger"

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

const char* psz_ary_sql_script[] = {
    // -- create db

    // -- 受影响的行: 1
    // -- drop database my_db_a;

    // -- 受影响的行: 1
    // -- create database my_db_a;

    // -- 受影响的行: 0
    // -- use my_db_a;

    // ora8iclent操作ora11gr2的删除表报错(ORA-24333: zero iteration count), 包没有发出去
    // 可以执行的sql语句类型
    // "select * from tbl_a",
    // "select * from tbl_a where id >= 1 and id <= 3",
    //
    // 不能执行的sql语句类型
    // delete "delete from tbl_a where id > 0",
    // "insert into tbl_a values (1,'name1','student','test user')",
    // "update tbl_a set note='6 row change' where id >= 1 and id <= 6",

    // "select * from tbl_a",
    // "select * from tbl_a where id >= 1 and id <= 1",
    // "select * from tbl_a where id >= 1 and id <= 2",
    // "select * from tbl_a where id >= 1 and id <= 3",
    // "select * from tbl_a where id >= 1 and id <= 4",
    // "select * from tbl_a where id >= 1 and id <= 5",
    // "select * from tbl_a where id >= 1 and id <= 6",

    // NULL,

    // -- create table
    "drop table tbl_a",

    // -- 受影响的行: 0
    "create table tbl_a (id int ,name varchar(260))",

    // -- 受影响的行: 0
    "alter table tbl_a add type varchar(260)",

    // -- 受影响的行: 0
    "alter table tbl_a add note varchar(260)",

    // -- 受影响的行: 0
    "delete from tbl_a where id > 0",

    // -- 受影响的行: 1
    "insert into tbl_a values (1,'name1','student','test user')",

    // -- 受影响的行: 1
    "insert into tbl_a values (2,'name2','student','test user')",

    // -- 受影响的行: 1
    "insert into tbl_a values (3,'name3','student','test user')",

    // -- 受影响的行: 1
    "insert into tbl_a values (4,'name4','student','test user')",

    // -- 受影响的行: 1
    "insert into tbl_a values (5,'name5','student','test user')",

    // -- 受影响的行: 1
    "insert into tbl_a values (6,'name6','student','test user')",

    // -- 受影响的行: 1
    "update tbl_a set note='1 row change' where id = 1",

    // -- 受影响的行: 2
    "update tbl_a set note='2 row change' where id >= 1 and id <= 2",

    // -- 受影响的行: 3
    "update tbl_a set note='3 row change' where id >= 1 and id <= 3",

    // -- 受影响的行: 4
    "update tbl_a set note='4 row change' where id >= 1 and id <= 4",

    // -- 受影响的行: 5
    "update tbl_a set note='5 row change' where id >= 1 and id <= 5",

    // -- 受影响的行: 6
    "update tbl_a set note='6 row change' where id >= 1 and id <= 6",

    // -- 不支持排序的select
    "select * from tbl_a",

    // -- 受影响的行: 1
    "delete from tbl_a where id >= 1 and id <= 1",

    "insert into tbl_a values (1,'name1','student','test user')",

    // -- 受影响的行: 2
    "delete from tbl_a where id >= 1 and id <= 2",

    "insert into tbl_a values (1,'name1','student','test user')",
    "insert into tbl_a values (2,'name2','student','test user')",

    // -- 受影响的行: 3
    "delete from tbl_a where id >= 1 and id <= 3",

    "insert into tbl_a values (1,'name1','student','test user')",
    "insert into tbl_a values (2,'name2','student','test user')",
    "insert into tbl_a values (3,'name3','student','test user')",

    // -- 受影响的行: 4
    "delete from tbl_a where id >= 1 and id <= 4",

    "insert into tbl_a values (1,'name1','student','test user')",
    "insert into tbl_a values (2,'name2','student','test user')",
    "insert into tbl_a values (3,'name3','student','test user')",
    "insert into tbl_a values (4,'name4','student','test user')",

    // -- 受影响的行: 5
    "delete from tbl_a where id >= 1 and id <= 5",

    "insert into tbl_a values (1,'name1','student','test user')",
    "insert into tbl_a values (2,'name2','student','test user')",
    "insert into tbl_a values (3,'name3','student','test user')",
    "insert into tbl_a values (4,'name4','student','test user')",
    "insert into tbl_a values (5,'name5','student','test user')",

    // -- 受影响的行: 6
    "delete from tbl_a where id >= 1 and id <= 6",

    "insert into tbl_a values (1,'name1','student','test user')",
    "insert into tbl_a values (2,'name2','student','test user')",
    "insert into tbl_a values (3,'name3','student','test user')",
    "insert into tbl_a values (4,'name4','student','test user')",
    "insert into tbl_a values (5,'name5','student','test user')",
    "insert into tbl_a values (6,'name6','student','test user')",

    "commit",

    NULL
};

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 do_oci_task_sql_script(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 do_oci_task_exec_one_sql(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 do_oci_task_exec_one_sql_bad(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 if (0 == strcmp(CMD_SQL_SCRIPT, sz_sql)) {
            printf("test sql script ...\n");
            do_oci_task_sql_script(sz_sql, h_oci_env, h_oci_server, h_oci_error, h_oci_context, h_oci_user);
            printf("test sql script END\n");
        }
        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_SQL_SCRIPT "', 执行预置的sql语句数组(sql_script.txt), 只执行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);
    }
}

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

    for (i = 0; i < sizeof(psz_ary_sql_script) / sizeof(psz_ary_sql_script[0]); i++) {
        if (NULL == psz_ary_sql_script[i]) {
            break;
        }

        memset(sz_sql, 0, sizeof(sz_sql));
        sprintf(sz_sql, "%s", psz_ary_sql_script[i]);
        printf("test sql_script [%d] : execut sql \"%s\" ...\r\n", i, sz_sql);
        do_oci_task_exec_one_sql(sz_sql, h_oci_env, h_oci_server, h_oci_error, h_oci_context, h_oci_user);
    }
}

void do_oci_task_exec_one_sql_bad(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;
        }
    } while (0);

    // 释放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);
    }
}

void do_oci_task_exec_one_sql(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 = OCI_STMT_SELECT; // 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_exec_one_sql\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) {
            // ora10通过sid连接ora8.1, 执行除了select语句之外的SQL, 都会得到下列错误
            // ORA-24333: 零迭代计数
            print_oci_error("error : OCIStmtPrepare", h_oci_error);
            break;
        }

        // 必须在执行SQL语句前, 获取SQL语句类型用于处理参数4, 防止出现错误 : ORA-24333: zero iteration count
        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;
        }

        // 执行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");
        // 处理参数4, 防止报错; ORA-24333
        sw_rc = OCIStmtExecute(
            h_oci_context, 
            h_oci_stmt, 
            h_oci_error, 
            (ub4)(OCI_STMT_SELECT == stmt_type ? 0 : 1), 
            (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();

        // 取结果集列信息
        if (OCI_STMT_SELECT == stmt_type) {
            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;
            }

            g_i_columns_cnt = fieldCount;

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

                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;

                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);

                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;
                    }

                    dtypeNew = SQLT_CHR;
                }
                break;
                default:
                    // assert(0);
                    break;
                }

                g_col_info[i_index].m_i_data_type_now = (int)dtypeNew;

                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;
                }
            }
        }

        // 遍历行集
        i_row_index = 0;
        do {
            if (OCI_STMT_SELECT != stmt_type) {
                // 只有select语句有行集可以遍历
                break;
            }

            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代表行集读完了
                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);

    // 释放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/80153463