Quickstart: Maak verbinding en voer query's uit met C++ en ODBC

Van toepassing op:SQL ServerAzure SQL Database

In deze quickstart bouw je een C++-consoleapplicatie op Windows, Linux of macOS. De applicatie maakt verbinding met een AdventureWorksLT database door Microsoft ODBC Driver 18 voor SQL Server te gebruiken, bindt een queryparameter, voert de query uit en leest de resulterende rijen.

Prerequisites

  • Microsoft ODBC Driver 18 voor SQL Server. Installeer de driver voor Windows, Linux of macOS.
  • Een C++17-compiler en de ODBC-ontwikkelingsbestanden van het platform:
    • Installeer op Windows Visual Studio 2022 of de Build Tools voor Visual Studio 2022 met de workload Desktop development with C++. De Windows SDK levert de ODBC-headers en odbc32.lib.
    • Installeer op Linux een C++-compiler en het unixODBC-ontwikkelpakket voor je distributie. Het pakket levert de ODBC-headers en libodbc.
    • Op macOS installeer je de Xcode-commandoregeltools en unixODBC vanuit Homebrew.
  • Een database in Azure SQL Database of SQL Server die de AdventureWorksLT voorbeeldgegevens bevat. Voor Azure SQL Database selecteer je de voorbeeldgegevensbron wanneer je een enkele database aanmaakt. Voor SQL Server herstelt u een AdventureWorksLT back-up uit AdventureWorks voorbeelddatabases.

Controleer de bestuurder

Bevestig dat de driver manager Microsoft ODBC Driver 18 voor SQL Server kan vinden.

Voer dit commando uit in PowerShell:

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

Elk commando moet vermelden ODBC Driver 18 for SQL Server. Als de driver niet vermeld staat, installeer hem dan opnieuw voordat je doorgaat.

Configureer de verbinding

De applicatie leest de volledige verbindingsreeks uit de ODBC_CONNECTION_STRING omgevingsvariabele. Het toont de verbindingsreeks niet en accepteert deze niet als een commandoregelargument.

Gebruik een Driver 18-verbindingsreeks die geldig is voor je database en authenticatiemethode. Het volgende voorbeeld van SQL-authenticatie werkt op Windows, Linux en macOS wanneer de database SQL-authenticatie toestaat:

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

Voor Microsoft Entra-authenticatieopties, zie Gebruik Microsoft Entra ID met de ODBC-driver. Raadpleeg voor alle ondersteunde instellingen DSN en trefwoorden en kenmerken van verbindingsreeksen.

Microsoft ODBC Driver 18 schakelt standaard encryptie in. Specificeer Encrypt=yes zodat de eis van de applicatie expliciet is. Houd TrustServerCertificate=no ingeschakeld in productie zodat de driver het servercertificaat valideert. Het certificaat moet de servernaam en keten koppelen aan een certificeringsinstantie die de client vertrouwt. Voor configuratierichtlijnen, zie Certificaatvalidatiefouten.

Caution

TrustServerCertificate=yes Slaat certificaatvalidatie over. Gebruik het alleen voor geïsoleerde lokale ontwikkeling terwijl je een certificaat configureert dat de client vertrouwt. Gebruik het niet in productie.

Stel de omgevingsvariabele in zonder de verbindingsreeks toe te voegen aan je shellgeschiedenis.

Open Developer PowerShell voor VS 2022. Voer deze commando's uit en plak vervolgens de verbindingsreeks in de prompt:

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

Een omgevingsvariabele houdt de verbindingsreeks buiten het bronbestand, maar het proces en zijn kindprocessen kunnen deze lezen. Voor productieapplicaties gebruik waar mogelijk Microsoft Entra-authenticatie en haal tijdens runtime geheimen op uit een beveiligde opslag.

De toepassing maken

  1. Maak een projectmap aan en verander erin:

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

  1. Maak een bestand met de naam odbc-quickstart.cpp met de volgende code:

    #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;
    }
    

De applicatie gebruikt alleen de standaard ODBC API, dus bevat deze de driver-manager headers en links naar de driver-manager bibliotheek. De verbindingsreeks selecteert Microsoft ODBC Driver 18 voor SQL Server tijdens runtime.

SQL_DRIVER_NOPROMPT voorkomt SQLDriverConnect dat een configuratiedialoog wordt geopend. Als de verbindingsreeks onvolledig is, geeft de oproep een foutmelding terug en print de applicatie elk diagnostisch record.

De query bindt 0 als een SQL Server int-parameter, leest de eerste vijf producten in SalesLT.Product, en haalt elke product-ID en naam op met SQLGetData.

De toepassing bouwen en uitvoeren

  1. Compileer in hetzelfde Developer PowerShell-venster de applicatie:

    cl /std:c++17 /EHsc /W4 odbc-quickstart.cpp /link odbc32.lib
    
  2. Voer de toepassing uit:

    .\odbc-quickstart.exe
    

De standaardoutput van de applicatie is:

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

Maak de verbindingsreeks schoon als je klaar bent.

Remove-Item Env:\ODBC_CONNECTION_STRING