Diagnostyka ODBC

Skorzystaj z tych wskazówek ze sterownikiem Microsoft ODBC Driver 17 lub 18. Opisuje on kontrakt diagnostyczny ODBC; nie definiuje natywnych mapowań błędów specyficznych dla konkretnej wersji.

Funkcja ODBC zwraca kod opisujący wynik tego wywołania. Dokumentacja diagnostyczna dostarcza więcej szczegółów. Interfejs programowania aplikacji (API) może raportować rekordy z Menedżera Sterowników, sterownika, źródła danych lub więcej niż jednego komponentu. Nie traktuj kodu zwrotnego, SQLSTATE, natywnego kodu błędu i wiadomości za zamienne.

Interpretuj kod zwrotny

Sprawdź kod zwrotu przed przetwarzaniem wyjścia lub decyzją, czy spróbować ponownie. Dla SQL_ERROR i SQL_SUCCESS_WITH_INFO, pobierz diagnostykę z uchwytu używanego przez funkcję. Inne kody zwrotne również mogą mieć rekordy diagnostyczne, gdy pozwala na to kontrakt funkcji.

Kod powrotny Znaczenie i odpowiedź
SQL_SUCCESS Funkcja została pomyślnie ukończona. Sprawdź kontrakt danych wyjściowych funkcji.
SQL_SUCCESS_WITH_INFO Funkcja zakończyła się z informacją o ostrzeżeniu. Pobierz każdy rekord diagnostyczny i sprawdź, czy wyjście jest kompletne, przekonwertowane czy obcięte.
SQL_ERROR Funkcja zakończyła się niepowodzeniem. Pobierz każdy rekord diagnostyczny przed wywołaniem innej funkcji na tym samym uchwytie.
SQL_INVALID_HANDLE Funkcja wykrywała uchwyt zerowy lub uchwyt niewłaściwego typu. ODBC nie udostępnia rejestrów diagnostycznych dla tego kodu zwrotnego. Sprawdź opis funkcji pod kątem dodatkowych wymagań dotyczących deskryptorów.
SQL_NO_DATA Nie ma już dostępnych danych dotyczących tej operacji. Sprawdź referencję do funkcji, ponieważ znaczenie i wszelkie rekordy statusu zależą od funkcji.

Więcej informacji o pełnym opisie kodów zwrotnych można znaleźć w artykule ODBC return codes.

Pobierz każdy rekord diagnostyczny

Wywołaj SQLGetDiagRec dla rekordów stanu, począwszy od rekordu nr 1. Kontynuuj, aż funkcja zwróci SQL_NO_DATA, lub odczytaj liczbę rekordów z nagłówka rekordu 0, wywołując SQLGetDiagField z SQL_DIAG_NUMBER. SQLGetDiagRec zwraca SQLSTATE rekordu, natywny kod błędu oraz tekst wiadomości. Używaj SQLGetDiagField, gdy potrzebujesz innych pól nagłówka lub rekordu.

Nie zatrzymuj się po pierwszym rekordzie. Komponent ODBC może dodać rekord statusu z informacjami o własnej warstwie, zachowując jednocześnie diagnostykę z innego komponentu. Kolejność i zawartość rekordów mogą pomóc zidentyfikować, gdzie powstał błąd.

Zbieraj dokumenty niezwłocznie. Późniejsze wywołanie ODBC, które używa tego samego uchwytu, zastępuje obszar diagnostyczny, z wyjątkiem wywołań do SQLGetDiagRec, SQLGetDiagField, oraz funkcji dziedziczonej SQLError . Użyj SQLGetDiagRec i SQLGetDiagField w nowym kodzie.

SQLGetDiagRec może zwrócić SQL_SUCCESS_WITH_INFO , gdy bufor wiadomości jest zbyt mały. Rekord został zwrócony, ale tekst wiadomości został skrócony. Sprawdź zgłoszoną długość tekstu i pobierz wiadomość z większym buforem, jeśli potrzebujesz pełnego tekstu. Nie myl tego kodu zwrotnego z SQLGetDiagRec z kodem zwrotnym z oryginalnej funkcji ODBC.

Więcej informacji o pobieraniu diagnostyki można znaleźć w Diagnostic records, Using SQLGetDiagRec and SQLGetDiagField oraz w dokumentacji SQLGetDiagRec i SQLGetDiagField.

Zbieraj rekordy diagnostyczne w C++

Użyj tego pomocnika C++11 z istniejącym handlem ODBC. Wywołuje SQLGetDiagRecW w celu zebrania SQLSTATE, natywnego kodu błędu oraz tekstu wiadomości Unicode w pamięci bez logowania komunikatów.

#ifdef _WIN32
#include <windows.h>
#endif
#include <sql.h>
#include <sqlext.h>
#include <algorithm>
#include <limits>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

struct DiagnosticRecord {
    std::string sqlstate;
    SQLINTEGER native_error;
    std::vector<SQLWCHAR> message;
    bool message_truncated;
};

std::vector<DiagnosticRecord> ReadDiagnostics(SQLSMALLINT type, SQLHANDLE handle) {
    const SQLSMALLINT limit = (std::numeric_limits<SQLSMALLINT>::max)();
    std::vector<DiagnosticRecord> records;
    for (SQLINTEGER number = 1; ; ++number) {
        if (number > limit) {
            throw std::runtime_error("Diagnostic record number exceeds SQLSMALLINT.");
        }
        SQLWCHAR state[6] = {};
        SQLINTEGER native = 0;
        std::vector<SQLWCHAR> message(256);
        auto read = [&]() {
            return SQLGetDiagRecW(type, handle, static_cast<SQLSMALLINT>(number),
                state, &native, message.data(),
                static_cast<SQLSMALLINT>(message.size()), nullptr);
        };

        SQLRETURN result = read();
        if (result == SQL_NO_DATA) {
            return records;
        }
        if (result == SQL_SUCCESS_WITH_INFO) {
            message.resize(limit);
            result = read(); // Retrieve the same record, not the next one.
        }
        if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
            throw std::runtime_error("SQLGetDiagRecW couldn't retrieve a diagnostic record.");
        }
        message.erase(std::find(message.begin(), message.end(), SQLWCHAR{0}), message.end());
        records.push_back({
            std::string(state, state + 5),
            native,
            std::move(message),
            result == SQL_SUCCESS_WITH_INFO
        });
    }
}

Funkcja pomocnicza ponawia próbę z obciętą wiadomością o największej dodatniej długości bufora, którą akceptuje SQLGetDiagRecW. Jeśli wiadomość nadal nie pasuje, zachowuje dostępny tekst i wstawia znacznik message_truncated. Kontynuuje do następnego rekordu, zamiast traktować to obcięcie jako koniec listy diagnostycznej. Zgłasza wyjątek, jeśli pobieranie się nie powiodło lub numer rekordu przekracza zakres API SQLSMALLINT.

Każda wiadomość zawiera SQLWCHAR jednostek kodowych bez terminatora zerowego. Nie reinterpretuj ich jako wchar_t, ponieważ typy mogą mieć różne szerokości. Konwertuj je na kodowanie tekstowe swojej aplikacji, gdy musisz przetworzyć lub wyświetlić wiadomość.

Wywołaj funkcję pomocniczą natychmiast po operacji, która zwraca błąd lub ostrzeżenie. Ten fragment zakłada, że statement jest prawidłowym, przygotowanym uchwytem instrukcji i że asynchroniczne wykonanie jest wyłączone:

#include <iostream>

SQLRETURN ExecuteWithDiagnostics(SQLHSTMT statement) {
    SQLRETURN result = SQLExecute(statement);
    if (result == SQL_ERROR || result == SQL_SUCCESS_WITH_INFO) {
        const auto records = ReadDiagnostics(SQL_HANDLE_STMT, statement);
        for (const auto& record : records) {
            std::cerr << "SQLExecute: SQLSTATE=" << record.sqlstate
                      << " native_error=" << record.native_error
                      << " message_truncated=" << record.message_truncated << '\n';
        }
    } else if (result == SQL_INVALID_HANDLE) {
        throw std::runtime_error("SQLExecute returned SQL_INVALID_HANDLE.");
    }
    return result;
}

Opakowanie zachowuje oryginalny kod zwrotny, w tym SQL_SUCCESS_WITH_INFO. Stosuj ten sam wzór do innych wywołań ODBC, używając typu uchwytu i uchwytu powiązanego z tą operacją. Obsługuj inne kody zwrotne zgodnie z kontraktem tej funkcji.

Wynik celowo pomija record.message. Jeśli potrzebujesz tekstu wiadomości w logu, zanonimizuj go zgodnie z polityką danych aplikacji przed zapisaniem jej. Pomocnik nie sanityzuje wiadomości przechowywanych w pamięci.

Przeczytaj pola diagnostyczne

Pola odpowiadają na różne pytania:

Pole Co ci to mówi
SQLSTATE Pięcioznakowy kod identyfikujący ustandaryzowany stan diagnostyczny. Jej klasa to dwa pierwsze znaki.
Natywny kod błędu Wartość specyficzna dla źródła danych lub komponentu ODBC. To nie jest uniwersalny numer błędu i nie powinieneś zakładać, że to numer błędu SQL Server.
Tekst wiadomości Wyjaśnienie zrozumiałe dla człowieka. Czytaj go z kodem błędu SQLSTATE i natywnym kodem błędu; nie interpretuj go jako stabilny, maszynowo czytelny kontrakt.

Menedżer sterowników, sterownik i źródło danych mogą generować diagnostykę. Komponent, który dodaje rekord, nie może zastępować ani maskować błędu lub ostrzeżenia zgłaszanego przez inny komponent. Natywny kod błędu może być niedostępny lub nie mieć użytecznego znaczenia dla diagnostyki generowanej przez komponent. Więcej informacji o regułach komponentów można znaleźć w sekcji Reguły obsługi diagnostycznej. Więcej informacji o klasach i wartościach SQLSTATE można znaleźć w SQLSTATEs.

Używaj rekordów razem z kodem zwrotu funkcji i operacją. Na przykład SQL_SUCCESS_WITH_INFO oznacza, że funkcja została ukończona, ale nie gwarantuje, że każda wartość wyjściowa jest kompletna. Ostrzeżenie o obcięciu wymaga sprawdzenia zwróconej długości i podjęcia decyzji, czy pobrać pozostałe dane. Więcej informacji można znaleźć w artykule Długość danych, długość bufora oraz obcinanie.

Bezpiecznie zbieraj dowody

W zgłoszeniu wsparcia zapisz nazwę funkcji, typ uchwytu, kod zwrotny oraz każdy rekord diagnostyczny. Uwzględnij wersje sterownika i Menedżera sterowników, system operacyjny, architekturę oraz typ źródła danych. Zamaskuj dane uwierzytelniające, tokeny dostępu, sekrety łańcuchów połączeń, teksty zapytań, wartości parametrów oraz dane osobowe lub regulowane przed zapisaniem lub udostępnieniem danych wyjściowych. Wiadomości diagnostyczne i logi śledzenia mogą zawierać dane aplikacyjne.

Ogranicz śledzenie do najkrótszej potrzebnej reprodukcji. Przechowuj logi w katalogu dostępnym tylko dla konta uruchamiającego aplikację i używaj jawnej ścieżki logów zamiast współdzielonej lub tymczasowej domyślnej. Przestań śledzić po zakończeniu odtwarzania, a następnie usuń plik śledzenia, gdy już go nie potrzebujesz.

Wybierz warstwę śledzenia

Diagnostyka aplikacji i śledzenie odpowiadają na różne pytania:

Warstwa Co przechwytuje Użyj go do
Diagnostyka aplikacji Kody zwrotne i rekordy diagnostyczne z poszczególnych wywołań ODBC Zidentyfikuj funkcję, która zawiodła lub zwróciła ostrzeżenie, i zachowaj wszystkie jej pola diagnostyczne.
Śledzenie ODBC Driver Manager Wywołania API ODBC pomiędzy aplikacją, Menedżerem sterowników a sterownikiem Sprawdź, które wywołania wykonała aplikacja i jakie kody zwrotne wróciły. Argumenty śledzenia mogą ujawniać wartości wrażliwe.
Ślad właściwy dla sterownika Wewnętrzne zdarzenia sterownika, gdy sterownik dokumentuje obsługiwany mechanizm sterowania Zbadaj zachowanie sterownika, którego ślad interfejsu API nie może pokazać. Nie zakładaj, że śledzenie Driver Manager włącza tę funkcję.

W systemie Windows Menedżer sterowników ODBC obsługuje śledzenie za pomocą karty Śledzenie w Administratorze źródeł danych ODBC oraz za pomocą atrybutów SQL_ATTR_TRACE i SQL_ATTR_TRACEFILE. Śledzenie jest domyślnie wykonywane dla każdego użytkownika. Śledzenie na całej maszynie wymaga lokalnych uprawnień administratora i może przechwytywać wywołania z aplikacji działających pod innymi lokalnymi tożsamościami. Preferuj śledzenie dla użytkownika i zatrzymaj je zaraz po zakończeniu odtwarzania problemu. Więcej informacji można znaleźć w artykule Ustawienia opcji śledzenia, Włączenie śledzenia oraz SQLSetConnectAttr.

Na Linuksie i macOS zobacz śledzenie dostępu do danych ze sterownikiem ODBC na Linuksie i macOS dla konfiguracji menedżera sterowników unixODBC.

Istniejące wytyczne programistyczne dla Linuksa i macOS zachowują historyczne stwierdzenie, że wbudowane śledzenie diagnostyki (BID) nie zostało zweryfikowane przed wersją 17.3 sterownika ODBC. Ta notatka nie opisuje obecnej specyficznej dla sterownika kontroli śledzenia. Procedura unixODBC przechwytuje wywołania API Driver Managera, a nie zdarzenia BID.