2016-12-02 81 views
1

我執行一個簡單的INSERT語句與使用SQLBindParameter功能結合一個參數(SQL_BIGINT)任何錯誤。執行失敗,但沒有錯誤診斷。命令如下:甲骨文ODBC插入失敗沒有診斷

INSERT INTO oracle_test_table (id) VALUES (?) 

如果我使用SQLGetDiagField得到SQL_DIAG_NUMBER(錯誤診斷數) - 這是0,並試圖檢索使用SQLGetDiagRec沒有任何返回任何診斷。

最少例如:

#include <cstdint> 
#include <cstdio> 
#include <string> 

#include <sql.h> 
#include <sqlext.h> 

/** Oracle ODBC insert fail without error diagnostic test. 
* ====================================================== 
* 
* compile with: g++ oracle_test.cpp -o oracle_test -lodbc -std=c++11 
*/ 


// Extract error diagnostic. 
void extractDiag(SQLSMALLINT handleType, SQLHANDLE& handle, std::string& s) { 
    SQLINTEGER i = 1; 
    SQLINTEGER native; 
    SQLCHAR state[7]; 
    SQLCHAR text[512]; 
    SQLSMALLINT len; 
    SQLRETURN ret; 

    while(true) { 
     ret = SQLGetDiagRecA(handleType, handle, i++, state, &native, text, sizeof(text), &len); 
     if (!SQL_SUCCEEDED(ret)) 
      break; 
     s += "["; 
     s += reinterpret_cast<char*>(state); 
     s += "] (native "; 
     s += std::to_string(native); 
     s += "): "; 
     s += reinterpret_cast<char*>(text); 
     s += "; "; 
    } 
} 

// Allocate a statement handle. 
int allocateStatement(SQLHDBC& dbConn, SQLHSTMT& stmt) { 
    SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_STMT, dbConn, &stmt); 
    if (!SQL_SUCCEEDED(ret)) { 
     std::string s("statement allocation failed: \n"); 
     extractDiag(SQL_HANDLE_DBC, dbConn, s); 
     fprintf(stderr, "%s\n", s.c_str()); 
     return 1; 
    } 
    return 0; 
} 

int main() { 
    SQLRETURN ret; 
    SQLHENV env; 
    SQLHDBC dbConn; 
    SQLHSTMT stmt; 

    // Allocate an environment handle. 
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); 

    // Use ODBC version 3. 
    SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void*) SQL_OV_ODBC3, 0); 

    // Allocate a connection handle. 
    ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbConn); 

    // Set connection attributes. 
    SQLSetConnectAttr(dbConn, SQL_ATTR_QUIET_MODE, 0, SQL_IS_POINTER); 
    SQLSetConnectAttr(dbConn, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)60, SQL_IS_UINTEGER); 
    SQLSetConnectAttr(dbConn, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER)60, SQL_IS_UINTEGER); 

    // Connection string. 
    char connStr[] = "UID=user;PWD=pass;DRIVER={Oracle 12c ODBC driver};Dbq=192.168.15.1:1521/dbora;"; // Oracle 
    //char connStr[] = "UID=user;PWD=pass;DRIVER={PostgreSQL Unicode};Server=192.168.15.1;Database=dbpg;"; // PostgreSQL 
    //char connStr[] = "UID=user;PWD=pass;DRIVER={MySQL ODBC 5.3 Unicode Driver};Server=192.168.15.1;Database=dbmy;"; // MySQL 

    // Connect. 
    ret = SQLDriverConnectA(dbConn, 0, reinterpret_cast<SQLCHAR*>(connStr), sizeof(connStr)-1, 0, 0, 0, SQL_DRIVER_NOPROMPT); 
    if (!SQL_SUCCEEDED(ret)) { 
     std::string s("connection failed: \n"); 
     extractDiag(SQL_HANDLE_DBC, dbConn, s); 
     fprintf(stderr, "%s\n", s.c_str()); 
     return 1; 
    } 

    char dropTable[] = "DROP TABLE oracle_test_table"; 
    char createTable[] = "CREATE TABLE oracle_test_table (id int)"; 
    char insert[] = "INSERT INTO oracle_test_table (id) VALUES (?)"; 

    // Drop table in case it exists. 
    if (allocateStatement(dbConn, stmt)) 
     return 1; 
    ret = SQLExecDirectA(stmt, reinterpret_cast<SQLCHAR*>(dropTable), sizeof(dropTable)-1); 
    // could fail if there's no table... ignored 
    SQLFreeHandle(SQL_HANDLE_STMT, stmt); 

    // Create new table. 
    if (allocateStatement(dbConn, stmt)) 
     return 1; 
    ret = SQLExecDirectA(stmt, reinterpret_cast<SQLCHAR*>(createTable), sizeof(createTable)-1); 
    SQLFreeHandle(SQL_HANDLE_STMT, stmt); 

    // Allocate handle for the INSERT statement. 
    if (allocateStatement(dbConn, stmt)) 
     return 1; 

    // Bind BIGINT parameter. 
    int64_t ival = 2; 
    ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 19, 0, &ival, sizeof(int64_t), 0); 
    if (!SQL_SUCCEEDED(ret)) { 
     fprintf(stderr, "int binding failed\n"); 
     return 1; 
    } 

    // Execute the INSERT statement. 
    ret = SQLExecDirectA(stmt, reinterpret_cast<SQLCHAR*>(insert), sizeof(insert)-1); 
    if (!SQL_SUCCEEDED(ret)) { 
     fprintf(stderr, "insert failed\n"); 

     SQLULEN diagCount = 0; 
     ret = SQLGetDiagField(SQL_HANDLE_STMT, stmt, 0, SQL_DIAG_NUMBER, &diagCount, SQL_IS_UINTEGER, 0); 
     if (!SQL_SUCCEEDED(ret)) 
      fprintf(stderr, "failed retrieving error diagnostic count\n"); 
     else 
      fprintf(stderr, "error diagnostic count: %lu\n", diagCount); 

     std::string s("error diagnostics:\n"); 
     extractDiag(SQL_HANDLE_STMT, stmt, s); 
     fprintf(stderr, "%s\n", s.c_str()); 

     return 1; 
    } 

    return 0; 
} 

其它命令,包括CREATE TABLE,DROP TABLE或直接在命令與值INSERT(而不是使用SQLBindParameter結合)執行沒有任何問題。而錯誤診斷檢索否則工作完全正常。例如試圖除去不存在的表時,正確返回:

[42S02] (native 942): [Oracle][ODBC][Ora]ORA-00942: table or view does not exist 

但在這裏,沒有獲取返回。

你們當中有誰遇到過這樣的問題,執行命令失敗,但沒有錯誤診斷可用?

我使用 「甲骨文12C ODBC驅動程序」(即時客戶端12.1.0.2.0)上的Xubuntu 16.04 64位。使用ODBC版本3。該數據庫是「Oracle數據庫12c的企業版發行12.1.0.2.0 64位」在CentOS 7

相同的代碼工作正常在PostgreSQL 9.2.15運行(驅動「的Unicode的PostgreSQL」 9.3.300),

,並與MySQL 50年5月5日(驅動程序 「MySQL的ODBC 5.3 Unicode的驅動程序」 5.3.6)。

+1

你提交INSERT?其他自動提交設置? – jarlh

+0

默認情況下,ODBC設置爲自動提交。如果你關閉自動提交,結果是一樣的。 – omusil

+0

請編輯您的問題,包括其失敗的INSERT語句,它試圖在C++代碼來處理它包括的SQLBindParameter調用等感謝。 –

回答

1

我終於找到了爲什麼這在this other question發生。顯然Oracle ODBC驅動程序doesn't support binding of BIGINT parameters。壞事是,它只是不會給你任何有關它失敗原因的錯誤信息。

所以,如果你想在這個例子中使用64位整數參數(SQL_C_UBIGINTSQL_C_SBIGINT),你必須將它們綁定爲字符串代替,如:

int64_t val = 123456789; 
char* cstr = convert_to_string(val); 
size_t len = strlen(cstr); 
SQLLEN ind = len; 
SQLRETURN ret = SQLBindParameter(hstmt, column, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, len, 0, cstr, len, &ind);