SQL Server ODBC Driver not raising errors

一个人想着一个人 提交于 2019-11-28 13:54:34

SQL Server returns DONE_IN_PROC messages to the client after each statement unless you specify SET NOCOUNT ON. These messages (row counts) will affect ADO applications that are not coded to handle the multiple record sets returned by calling the NextRecordset method.

The easy solution is to specify SET NOCOUNT ON as the first statement in the batch or stored procedure.

I did not read your question carefully, therefore my comment referred to the wrong ODBC driver.

Anyway, I tested it using three different ODBC drivers against SQL Server 2014:

  • SQL Server Native Client 11.0
  • ODBC Driver 11 for SQL Server
  • SQL Server

Updated: With all three drivers, if I execute only one insert, I get the expected error in the form:

ERROR; native: 515; state: 23000; msg: [Microsoft][ODBC SQL Server Driver][SQLServer]Cannot insert the value NULL into column 'WalletID', table 'Test.dbo.Walle t'; column does not allow nulls. INSERT fails. ERROR; native: 3621; state: 01000; msg: [Microsoft][ODBC SQL Server Driver][SQL Server]The statement has been terminated.

If I execute multiple inserts in one statement, and the first insert does not fail, no error is raised, as mentioned in the comment by Ian Boyd.

Workaround: Turn of auto-commit, but enable manual commit. Execution of the multiple inserts will still not fail, but committing the transaction will raise an error. Unfortunately, I was not able to retrieve any error information, but at least you get an SQL_ERROR as result when trying to commit.

Here is the updated sample code:

#include <windows.h>
#include <tchar.h>
#include <iostream>
#include <sql.h>
#include <sqlext.h>
#include <sqlucode.h>
#include <functional>

void printErr(SQLHANDLE handle, SQLSMALLINT handleType)
{
    SQLSMALLINT recNr = 1;
    SQLRETURN ret = SQL_SUCCESS;
    while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
    {
        SQLWCHAR errMsg[SQL_MAX_MESSAGE_LENGTH + 1];
        SQLWCHAR sqlState[5 + 1];
        errMsg[0] = 0;
        SQLINTEGER nativeError;
        SQLSMALLINT cb = 0;
        ret = SQLGetDiagRec(handleType, handle, recNr, sqlState, &nativeError, errMsg, SQL_MAX_MESSAGE_LENGTH + 1, &cb);
        if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
        {
            std::wcerr << L"ERROR; native: " << nativeError << L"; state: " << sqlState << L"; msg: " << errMsg << std::endl;
        }
        ++recNr;
    }
}


int _tmain(int argc, _TCHAR* argv[])
{
    SQLHSTMT stmt = SQL_NULL_HSTMT;

    SQLRETURN   nResult = 0;
    SQLHANDLE   handleEnv = 0;

    nResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, (SQLHANDLE*)&handleEnv);
    nResult = SQLSetEnvAttr(handleEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3_80, SQL_IS_INTEGER);

    SQLHANDLE   handleDBC = 0;
    nResult = SQLAllocHandle(SQL_HANDLE_DBC, handleEnv, (SQLHANDLE*)&handleDBC);

//  SQLWCHAR     strConnect[256] = L"Driver={SQL Server Native Client 11.0};Server=.\\TEST;Database=Test;Trusted_Connection=yes;";
//  SQLWCHAR     strConnect[256] = L"Driver={ODBC Driver 11 for SQL Server};Server=.\\TEST;Database=Test;Trusted_Connection=yes;";
    SQLWCHAR     strConnect[256] = L"Driver={SQL Server};Server=.\\TEST;Database=Test;Trusted_Connection=yes;";
    SQLWCHAR     strConnectOut[1024] = { 0 };
    SQLSMALLINT nNumOut = 0;
    nResult = SQLDriverConnect(handleDBC, NULL, (SQLWCHAR*)strConnect, SQL_NTS, (SQLWCHAR*)strConnectOut, sizeof(strConnectOut),
        &nNumOut, SQL_DRIVER_NOPROMPT);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErr(handleDBC, SQL_HANDLE_DBC);
    }

    // Enable manual commit
    nResult = SQLSetConnectAttr(handleDBC, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErr(handleDBC, SQL_HANDLE_DBC);
    }

    SQLHSTMT    handleStatement = 0;
    nResult = SQLAllocHandle(SQL_HANDLE_STMT, handleDBC, (SQLHANDLE*)&handleStatement);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErr(handleDBC, SQL_HANDLE_DBC);
    }

    // try to drop, ignore if it exists
    SQLExecDirect(handleStatement, L"DROP TABLE Wallet", SQL_NTS);

    nResult = SQLExecDirect(handleStatement, L"CREATE TABLE Wallet (WalletID int NOT NULL,  Name varchar(50) NOT NULL)", SQL_NTS);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErr(handleStatement, SQL_HANDLE_STMT);
    }

    //nResult = SQLExecDirect(handleStatement, L"INSERT INTO Wallet (WalletID, Name) VALUES (2, 'Fan') INSERT INTO Wallet (WalletID, Name) VALUES (1, 'Fan')", SQL_NTS);
    nResult = SQLExecDirect(handleStatement, L"INSERT INTO Wallet (WalletID, Name) VALUES (2, 'Fan') INSERT INTO Wallet (Name) VALUES ('Fan')", SQL_NTS);
    //nResult = SQLExecDirect(handleStatement, L"INSERT INTO Wallet (Name) VALUES ('Fan')", SQL_NTS);
    //nResult = SQLExecDirect(handleStatement, L"INSERT INTO Wallet (WalletID, Name) VALUES (1, 'Fan')", SQL_NTS);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErr(handleStatement, SQL_HANDLE_STMT);
    }

    // Try to end transaction. Will fail, but no error information can be fetched(?)
    nResult = SQLEndTran(SQL_HANDLE_DBC, handleDBC, SQL_COMMIT);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErr(handleDBC, SQL_HANDLE_DBC);
    }

    return 0;
}
Sam

The WalletID int column is not set to auto-increment.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!