Avvio rapido: Connetti e interroga con C++ e ODBC

Si applica a:SQL ServerDatabase SQL di Azure

In questo avvio rapido, costruisci un'applicazione console C++ su Windows, Linux o macOS. L'applicazione si collega a un AdventureWorksLT database utilizzando il driver ODBC 18 di Microsoft per SQL Server, associa un parametro di interrogazione, esegue la query e legge le righe dei risultati.

Prerequisites

  • Microsoft ODBC Driver 18 per SQL Server. Installa il driver per Windows, Linux o macOS.
  • Un compilatore C++17 e i file di sviluppo ODBC della piattaforma:
    • In Windows, installa Visual Studio 2022 o Build Tools per Visual Studio 2022 con il carico di lavoro Sviluppo desktop con C++. Windows SDK fornisce gli header ODBC e odbc32.lib.
    • Su Linux, installa un compilatore C++ e il pacchetto di sviluppo unixODBC per la tua distribuzione. Il pacchetto fornisce le intestazioni ODBC e libodbc.
    • Su macOS, installa gli strumenti di riga di comando di Xcode e unixODBC da Homebrew.
  • Un database in database SQL di Azure o SQL Server che contiene i AdventureWorksLT dati di esempio. Per database SQL di Azure, seleziona la fonte di dati di esempio quando crei un singolo database. Per SQL Server, ripristina un AdventureWorksLT backup dai database di esempio di AdventureWorks.

Verificare il driver

Conferma che il driver manager riesca a trovare il driver Microsoft ODBC 18 per SQL Server.

Esegui questo comando in PowerShell:

Get-OdbcDriver -Name "ODBC Driver 18 for SQL Server"

Ogni comando dovrebbe elencare ODBC Driver 18 for SQL Server. Se il driver non è elencato, reinstallalo prima di continuare.

Configura la connessione

L'applicazione legge l'intera stringa di connessione dalla ODBC_CONNECTION_STRING variabile di ambiente. Non mostra la stringa di connessione né la accetta come argomento da riga di comando.

Usa una stringa di connessione Driver 18 valida per il tuo database e il metodo di autenticazione. Il seguente esempio di autenticazione SQL funziona su Windows, Linux e macOS quando il database consente l'autenticazione SQL:

Driver={ODBC Driver 18 for SQL Server};Server=tcp:<server>,1433;Database=<database>;UID=<user_id>;PWD=<password>;Encrypt=yes;TrustServerCertificate=no;

Per le opzioni di autenticazione Microsoft Entra, vedi Usa Microsoft Entra ID con il driver ODBC. Per tutte le impostazioni supportate, consulta parole chiave e attributi DSN e della stringa di connessione.

Il driver ODBC 18 di Microsoft abilita la crittografia di default. Specifica Encrypt=yes in modo esplicito il requisito dell'applicazione. Mantieni TrustServerCertificate=no in produzione così il driver convalidi il certificato del server. Il certificato deve abbinare il nome e la catena del server a un'autorità di certificazione di cui il client si fida. Per indicazioni sulla configurazione, vedi Fallimenti di validazione dei certificati.

Caution

TrustServerCertificate=yes Salta la validazione del certificato. Usalo solo per lo sviluppo locale isolato mentre configuri un certificato di cui il client si fida. Non usarlo nell'ambiente di produzione.

Imposta la variabile environment senza aggiungere la stringa di connessione alla cronologia della tua shell.

OpenDeveloper PowerShell per VS 2022. Esegui questi comandi, poi incolla la stringa di connessione sul prompt:

$secureConnectionString = Read-Host "ODBC connection string" -AsSecureString
$env:ODBC_CONNECTION_STRING = [System.Net.NetworkCredential]::new(
    "", $secureConnectionString).Password
$secureConnectionString = $null

Una variabile di ambiente tiene la stringa di connessione fuori dal file sorgente, ma il processo e i suoi processi figli possono leggerla. Per le applicazioni in produzione, utilizzare l'autenticazione Microsoft Entra quando possibile e recuperare i segreti da un archivio sicuro in fase di esecuzione.

Creare l'applicazione

  1. Crea una directory di progetto e cambia la situazione:

    New-Item -ItemType Directory odbc-quickstart
    Set-Location odbc-quickstart
    

  1. Creare un file denominato odbc-quickstart.cpp con il codice seguente:

    #ifdef _WIN32
    #include <windows.h>
    #endif
    
    #include <sql.h>
    #include <sqlext.h>
    #include <sqltypes.h>
    
    #include <cstdlib>
    #include <iomanip>
    #include <iostream>
    #include <string>
    
    std::string ReadEnvironmentVariable(const char* name)
    {
    #ifdef _WIN32
        char* value = nullptr;
        std::size_t length = 0;
        if (_dupenv_s(&value, &length, name) != 0 || value == nullptr)
            return {};
    
        std::string result(value);
        std::free(value);
        return result;
    #else
        const char* value = std::getenv(name);
        return value == nullptr ? std::string{} : value;
    #endif
    }
    
    void PrintDiagnostics(SQLSMALLINT handleType, SQLHANDLE handle)
    {
        SQLCHAR state[6];
        SQLINTEGER nativeError;
        SQLCHAR message[SQL_MAX_MESSAGE_LENGTH];
        SQLSMALLINT messageLength;
    
        for (SQLSMALLINT record = 1;
             SQL_SUCCEEDED(SQLGetDiagRec(handleType, handle, record, state,
                                         &nativeError, message, sizeof(message),
                                         &messageLength));
             ++record)
        {
            std::cerr << '[' << state << "] (" << nativeError << ") "
                      << message << '\n';
        }
    }
    
    bool Succeeded(SQLRETURN result, SQLSMALLINT handleType, SQLHANDLE handle)
    {
        if (SQL_SUCCEEDED(result))
            return true;
    
        PrintDiagnostics(handleType, handle);
        return false;
    }
    
    struct OdbcHandles
    {
        SQLHENV environment = SQL_NULL_HENV;
        SQLHDBC connection = SQL_NULL_HDBC;
        SQLHSTMT statement = SQL_NULL_HSTMT;
    
        ~OdbcHandles()
        {
            if (statement != SQL_NULL_HSTMT)
                SQLFreeHandle(SQL_HANDLE_STMT, statement);
            if (connection != SQL_NULL_HDBC)
            {
                SQLDisconnect(connection);
                SQLFreeHandle(SQL_HANDLE_DBC, connection);
            }
            if (environment != SQL_NULL_HENV)
                SQLFreeHandle(SQL_HANDLE_ENV, environment);
        }
    };
    
    int main()
    {
        std::string connectionString =
            ReadEnvironmentVariable("ODBC_CONNECTION_STRING");
        if (connectionString.empty())
        {
            std::cerr << "Set ODBC_CONNECTION_STRING before running.\n";
            return 1;
        }
    
        OdbcHandles handles;
        SQLRETURN result = SQLAllocHandle(
            SQL_HANDLE_ENV, SQL_NULL_HANDLE, &handles.environment);
        if (!SQL_SUCCEEDED(result))
        {
            std::cerr << "Unable to allocate an ODBC environment handle.\n";
            return 1;
        }
    
        result = SQLSetEnvAttr(
            handles.environment,
            SQL_ATTR_ODBC_VERSION,
            reinterpret_cast<SQLPOINTER>(SQL_OV_ODBC3_80),
            0);
        if (!Succeeded(result, SQL_HANDLE_ENV, handles.environment))
            return 1;
    
        result = SQLAllocHandle(
            SQL_HANDLE_DBC, handles.environment, &handles.connection);
        if (!Succeeded(result, SQL_HANDLE_ENV, handles.environment))
            return 1;
    
        result = SQLDriverConnect(
            handles.connection,
            nullptr,
            reinterpret_cast<SQLCHAR*>(connectionString.data()),
            SQL_NTS,
            nullptr,
            0,
            nullptr,
            SQL_DRIVER_NOPROMPT);
        if (!Succeeded(result, SQL_HANDLE_DBC, handles.connection))
            return 1;
    
        result = SQLAllocHandle(
            SQL_HANDLE_STMT, handles.connection, &handles.statement);
        if (!Succeeded(result, SQL_HANDLE_DBC, handles.connection))
            return 1;
    
        SQLINTEGER minimumProductId = 0;
        SQLLEN minimumProductIdLength = 0;
        result = SQLBindParameter(
            handles.statement,
            1,
            SQL_PARAM_INPUT,
            SQL_C_SLONG,
            SQL_INTEGER,
            10,
            0,
            &minimumProductId,
            0,
            &minimumProductIdLength);
        if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement))
            return 1;
    
        SQLCHAR query[] =
            "SELECT TOP (5) ProductID, Name "
            "FROM SalesLT.Product "
            "WHERE ProductID > ? "
            "ORDER BY ProductID;";
        result = SQLExecDirect(handles.statement, query, SQL_NTS);
        if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement))
            return 1;
    
        std::cout << "Product ID  Name\n"
                  << "----------  ----\n";
    
        while (SQL_SUCCEEDED(result = SQLFetch(handles.statement)))
        {
            SQLINTEGER productId;
            SQLLEN productIdLength;
            SQLCHAR productName[256];
            SQLLEN productNameLength;
    
            result = SQLGetData(
                handles.statement, 1, SQL_C_SLONG, &productId,
                sizeof(productId), &productIdLength);
            if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement))
                return 1;
    
            result = SQLGetData(
                handles.statement, 2, SQL_C_CHAR, productName,
                sizeof(productName), &productNameLength);
            if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement))
                return 1;
    
            std::cout << std::left << std::setw(12) << productId
                      << productName << '\n';
        }
    
        if (result != SQL_NO_DATA)
        {
            PrintDiagnostics(SQL_HANDLE_STMT, handles.statement);
            return 1;
        }
    
        return 0;
    }
    

L'applicazione utilizza solo l'API ODBC standard, perciò include i file header del driver manager e si collega alla libreria del driver manager. La stringa di connessione seleziona il driver Microsoft ODBC 18 per SQL Server a runtime.

SQL_DRIVER_NOPROMPT impedisce SQLDriverConnect di aprire una finestra di dialogo di configurazione. Se la stringa di connessione è incompleta, la chiamata restituisce un errore e l'applicazione stampa ogni record diagnostico.

La query associa 0 a un parametro SQL Server int, legge i primi cinque prodotti presenti in SalesLT.Product e recupera l'ID e il nome di ciascun prodotto con SQLGetData.

Compilare ed eseguire l'applicazione

  1. Nella stessa finestra PowerShell per sviluppatori, compila l'applicazione:

    cl /std:c++17 /EHsc /W4 odbc-quickstart.cpp /link odbc32.lib
    
  2. Eseguire l'applicazione:

    .\odbc-quickstart.exe
    

L'output standard dell'applicazione è:

Product ID  Name
----------  ----
680         HL Road Frame - Black, 58
706         HL Road Frame - Red, 58
707         Sport-100 Helmet, Red
708         Sport-100 Helmet, Black
709         Mountain Bike Socks, M

Elimina la stringa di connessione quando hai finito.

Remove-Item Env:\ODBC_CONNECTION_STRING