處理 ODBC 錯誤 (ODBC)

擷取 ODBC 訊息可以使用兩個 ODBC 函數呼叫:SQLGetDiagRecSQLGetDiagField。 若要在 SQLStatepfNativeErrorMessage 診斷欄位中取得與主要 ODBC 相關的資訊,請呼叫 SQLGetDiagRec,直到它傳回 SQL_NO_DATA 為止。 對於每個診斷記錄,可以呼叫 SQLGetDiagField 來擷取個別的欄位。 所有驅動程式專用的欄位都必須使用 SQLGetDiagField 擷取。

SQLGetDiagRecSQLGetDiagField 是透過 ODBC 驅動程式管理員,而非個別的驅動程式處理。 在成功建立連接之前,ODBC 驅動程式管理員不會快取驅動程式專用的診斷欄位。 在成功連接之前,無法針對驅動程式專用的診斷欄位呼叫 SQLGetDiagField。 這包含 ODBC 連接命令,即使它們傳回 SQL_SUCCESS_WITH_INFO 也一樣。 在下次呼叫 ODBC 函數之前,將無法使用驅動程式專用的診斷欄位。

範例

說明

此範例會顯示呼叫標準 ODBC 資訊之 SQLGetDiagRec 的簡單錯誤處理常式。 然後它會測試是否有有效連接,如果有,就會為 SQL Server ODBC 驅動程式專用的診斷欄位呼叫 SQLGetDiagField。IA64 不支援此範例。

此範例是針對 ODBC 3.0 版或更新版本所開發。

安全性注意事項安全性注意事項

盡可能使用 Windows 驗證。 如果無法使用 Windows 驗證,請提示使用者在執行階段輸入認證。 請避免將認證儲存在檔案中。 如果您必須保存認證,則應該用 Win32 crypto API 加密這些認證。

您需要名為 AdventureWorks 的 ODBC 資料來源,其預設資料庫為 AdventureWorks 範例資料庫 (您可以從 Microsoft SQL Server 範例和社群專案首頁 (英文) 下載 AdventureWorks 範例資料庫)。此資料來源必須以作業系統提供的 ODBC 驅動程式為基礎 (驅動程式名稱為 "SQL Server")。 如果您要建立並執行此範例,當做 64 位元作業系統上的 32 位元應用程式,您必須利用 %windir%\SysWOW64\odbcad32.exe,以 ODBC 管理員身分建立 ODBC 資料來源。

這個範例會連接到電腦的預設 SQL Server 執行個體。 若要連接到具名執行個體,請變更 ODBC 資料來源的定義,以便使用下列格式指定執行個體:server\namedinstance。 根據預設,SQL Server Express 會安裝至具名執行個體。

執行第一個 (Transact-SQL) 程式碼清單,以便建立此範例所使用的預存程序。

使用 odbc32.lib 編譯第二個 (C++) 程式碼清單。 然後,執行此程式。

執行第三個 (Transact-SQL) 程式碼清單,以便刪除此範例所使用的預存程序。

程式碼

use AdventureWorks
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'BadOne')
   DROP PROCEDURE BadOne

Go

CREATE PROCEDURE BadOne 
AS 
SELECT * FROM Purchasing.Vendor
Go

程式碼

// compile with: odbc32.lib
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <odbcss.h>

#define MAXBUFLEN 256

SQLHENV henv = SQL_NULL_HENV;
SQLHDBC hdbc1 = SQL_NULL_HDBC;     
SQLHSTMT hstmt1 = SQL_NULL_HSTMT;

void ProcessLogMessages(SQLSMALLINT plm_handle_type, SQLHANDLE plm_handle, char *logstring, int ConnInd);

void Cleanup() {
   if (hstmt1 != SQL_NULL_HSTMT)
      SQLFreeHandle(SQL_HANDLE_STMT, hstmt1);

   if (hdbc1 != SQL_NULL_HDBC) {
      SQLDisconnect(hdbc1);
      SQLFreeHandle(SQL_HANDLE_DBC, hdbc1);
   }

   if (henv != SQL_NULL_HENV)
      SQLFreeHandle(SQL_HANDLE_ENV, henv);
}

int main() {
   RETCODE retcode;

   // Allocate the ODBC environment and save handle.
   retcode = SQLAllocHandle (SQL_HANDLE_ENV, NULL, &henv);
   if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
      printf("SQLAllocHandle(Env) Failed\n\n");
      Cleanup();
      return(9);
   }

   // Notify ODBC that this is an ODBC 3.0 app.
   retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
   if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
      printf("SQLSetEnvAttr(ODBC version) Failed\n\n");
      Cleanup();
      return(9);    
   }

   // Allocate ODBC connection handle and connect.
   retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1);
   if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
      printf("SQLAllocHandle(hdbc1) Failed\n\n");
      Cleanup();
      return(9);
   }

   // This sample use Integrated Security. Please create the SQL Server 
   // DSN by using the Windows NT authentication. 
   retcode = SQLConnect(hdbc1, (UCHAR*)"AdventureWorks", SQL_NTS, (UCHAR*)"",SQL_NTS, (UCHAR*)"", SQL_NTS);
   if ( (retcode != SQL_SUCCESS) &&
      (retcode != SQL_SUCCESS_WITH_INFO) ) {
         ProcessLogMessages(SQL_HANDLE_DBC, hdbc1, "SQLConnect() Failed\n\n", FALSE);
         Cleanup();
         return(9);
   }
   else {
      ProcessLogMessages(SQL_HANDLE_DBC, hdbc1,
         "\nConnect Successful\n\n", FALSE);
   }

   // Allocate statement handle, and then execute command.
   retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1);
   if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) {
      ProcessLogMessages(SQL_HANDLE_DBC, hdbc1, "SQLAllocHandle(hstmt1) Failed\n\n", TRUE);
      Cleanup();
      return(9);
   }

   retcode = SQLExecDirect(hstmt1, (UCHAR*)"exec BadOne", SQL_NTS);
   if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) {
         ProcessLogMessages(SQL_HANDLE_STMT, hstmt1, "SQLExecute() Failed\n\n", TRUE);
         Cleanup();
         return(9);
   }

   // Clear any result sets generated.
   while ( ( retcode = SQLMoreResults(hstmt1) ) != SQL_NO_DATA )
      ;

   // Clean up. 
   SQLFreeHandle(SQL_HANDLE_STMT, hstmt1);
   SQLDisconnect(hdbc1);
   SQLFreeHandle(SQL_HANDLE_DBC, hdbc1);
   SQLFreeHandle(SQL_HANDLE_ENV, henv);
}

void ProcessLogMessages(SQLSMALLINT plm_handle_type, SQLHANDLE plm_handle, char *logstring, int ConnInd) {
   RETCODE plm_retcode = SQL_SUCCESS;
   UCHAR plm_szSqlState[MAXBUFLEN] = "", plm_szErrorMsg[MAXBUFLEN] = "";
   SDWORD plm_pfNativeError = 0L;
   SWORD plm_pcbErrorMsg = 0;
   SQLSMALLINT plm_cRecNmbr = 1;
   SDWORD plm_SS_MsgState = 0, plm_SS_Severity = 0;
   SQLINTEGER plm_Rownumber = 0;
   USHORT plm_SS_Line;
   SQLSMALLINT plm_cbSS_Procname, plm_cbSS_Srvname;
   SQLCHAR plm_SS_Procname[MAXNAME], plm_SS_Srvname[MAXNAME];

   if (logstring)
      printf(logstring);

   while (plm_retcode != SQL_NO_DATA_FOUND) {
      plm_retcode = SQLGetDiagRec(plm_handle_type, plm_handle, plm_cRecNmbr, 
                                  plm_szSqlState, &plm_pfNativeError, plm_szErrorMsg, 
                                  MAXBUFLEN - 1, &plm_pcbErrorMsg);

      // Note that if the application has not yet made a successful connection, 
      // the SQLGetDiagField information has not yet been cached by ODBC Driver Manager and 
      // these calls to SQLGetDiagField will fail.
      if (plm_retcode != SQL_NO_DATA_FOUND) {
         if (ConnInd) { 
            plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr,
                                                        SQL_DIAG_ROW_NUMBER, &plm_Rownumber,
                                                        SQL_IS_INTEGER, NULL);

            plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr,
                                           SQL_DIAG_SS_LINE, &plm_SS_Line, SQL_IS_INTEGER, NULL);

            plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr, 
                                           SQL_DIAG_SS_MSGSTATE, &plm_SS_MsgState,
                                           SQL_IS_INTEGER, NULL);

            plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr,
                                           SQL_DIAG_SS_SEVERITY, &plm_SS_Severity,
                                           SQL_IS_INTEGER, NULL);

            plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr,
                                           SQL_DIAG_SS_PROCNAME, &plm_SS_Procname,
                                           sizeof(plm_SS_Procname), &plm_cbSS_Procname);

            plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr,
                                           SQL_DIAG_SS_SRVNAME, &plm_SS_Srvname, 
                                           sizeof(plm_SS_Srvname), &plm_cbSS_Srvname);
         }

         printf("szSqlState = %s\n", plm_szSqlState);
         printf("pfNativeError = %d\n", plm_pfNativeError);
         printf("szErrorMsg = %s\n", plm_szErrorMsg);
         printf("pcbErrorMsg = %d\n\n", plm_pcbErrorMsg);

         if (ConnInd) {
            printf("ODBCRowNumber = %d\n", plm_Rownumber);
            printf("SSrvrLine = %d\n", plm_Rownumber);
            printf("SSrvrMsgState = %d\n", plm_SS_MsgState);
            printf("SSrvrSeverity = %d\n", plm_SS_Severity);
            printf("SSrvrProcname = %s\n", plm_SS_Procname);
            printf("SSrvrSrvname = %s\n\n", plm_SS_Srvname);
         }
      }

      plm_cRecNmbr++;   // Increment to next diagnostic record.
   }
}

程式碼

use AdventureWorks
DROP PROCEDURE BadOne
GO

請參閱

其他資源

ODBC 的使用說明主題