2016-12-02 2 views
1

Я выполняю простой оператор INSERT с одним параметром (SQL_BIGINT), связанным с помощью функции SQLBindParameter. Выполнение не выполняется, но диагностика ошибок отсутствует. Команда выглядит следующим образом:Вставка Oracle 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 

Но здесь ничего не возвращается.

Неужели кто-то из вас столкнулся с этой проблемой, вызвавшей выполнение команды, но не существует диагностической диагностики?

Я использую «драйвер ODBC Oracle 12c» (Instant Client 12.1.0.2.0) на Xubuntu 16.04 64bit. ODBC версии 3. БД "Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 64bit" работает на CentOS 7.

же код работает отлично с PostgreSQL 9.2.15 (драйвер "PostgreSQL Unicode" 9.3.300),

и с MySQL 5.5.50 (драйвер "MySQL ODBC 5.3 Unicode Driver" 5.3.6).

+1

ли вы фиксируете INSERT? Другие настройки автоматической фиксации? – jarlh

+0

По умолчанию ODBC настроен на автоматическую фиксацию. Если вы установите auto-commit off, результат будет таким же. – omusil

+0

Измените свой вопрос, чтобы включить инструкцию INSERT, которая не работает, код C++, который пытается обработать его, включая вызовы SQLBindParameter и т. Д. Спасибо. –

ответ

1

Я, наконец, нашел, почему это произошло в this other question. Очевидно, Oracle ODBC драйвер doesn't support binding of BIGINT parameters. Плохо то, что он просто не дает вам никакой информации об ошибке, почему она не удалась.

Таким образом, в случае, если вы хотите использовать 64-разрядные целые параметры (SQL_C_UBIGINT или SQL_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); 
Смежные вопросы