Rychlý start: Připojte se a dotazujte pomocí C++ a ODBC

platí pro:SQL Serverazure SQL Database

V tomto rychlém startu vytvoříte konzolovou aplikaci pro C++ na Windows, Linuxu nebo macOS. Aplikace se připojuje k databázi AdventureWorksLT pomocí Microsoft ODBC Driver 18 for SQL Server, připojí parametr dotazu, vykoná dotaz a přečte výsledky řádků.

Prerequisites

  • Microsoft ODBC Driver 18 pro SQL Server. Nainstalujte ovladač pro Windows, Linux nebo macOS.
  • Kompilátor C++17 a vývojové soubory platformy ODBC:
    • Na Windows si nainstalujte Visual Studio 2022 nebo Build Tools for Visual Studio 2022 s desktopovým vývojem s C++ zátěží. Windows SDK poskytuje hlavičky ODBC a odbc32.lib.
    • Na Linuxu nainstalujte C++ kompilátor a vývojový balíček unixODBC pro vaši distribuci. Balíček poskytuje hlavičky ODBC a libodbc.
    • Na macOS nainstalujte příkazové nástroje Xcode a unixODBC z Homebrew.
  • Databáze v Azure SQL Database nebo SQL Server, která obsahuje ukázková AdventureWorksLT data. Pro Azure SQL Database vyberte vzorový datový zdroj při vytváření jedné databáze. V serveru SQL Server obnovte zálohu AdventureWorksLT z ukázkových databází AdventureWorks.

Ověřte ovladač

Ověřte, že správce ovladačů najde Microsoft ODBC ovladač 18 pro SQL Server.

Spusť tento příkaz v PowerShellu:

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

Každý příkaz by měl uvádět ODBC Driver 18 for SQL Server. Pokud ovladač není uveden, přeinstalujte ho před pokračováním.

Konfigurujte spojení

Aplikace čte kompletní připojovací řetězec z proměnné prostředí ODBC_CONNECTION_STRING. Nezobrazuje připojovací řetězec ani jej nepřijímá jako argument příkazové řádky.

Použijte připojovací řetězec pro Driver 18, který je platný pro vaši databázi a metodu ověřování. Následující příklad SQL autentizace funguje na Windows, Linuxu a macOS, pokud databáze umožňuje SQL autentizaci:

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

Možnosti ověřování Microsoft Entra ID najdete v článku Použití Microsoft Entra ID s ovladačem ODBC. Všechna podporovaná nastavení najdete v tématu Klíčová slova a atributy DSN a připojovacího řetězce.

Microsoft ODBC Driver 18 umožňuje šifrování ve výchozím nastavení. Specifikujte Encrypt=yes požadavky aplikace tak, aby byly explicitní. V produkčním prostředí ponechte TrustServerCertificate=no, aby ovladač ověřoval certifikát serveru. Certifikát musí odpovídat názvu serveru a v řetězci důvěry navazovat na certifikační autoritu, které klient důvěřuje. Pro pokyny k konfiguraci viz Selhání ověřování certifikátů.

Caution

TrustServerCertificate=yes Vynechá validaci certifikátu. Používejte ho pouze pro izolovaný lokální vývoj, zatímco konfigurujete certifikát, kterému klient důvěřuje. Nepoužívejte ho v produkčním prostředí.

Nastavte proměnnou prostředí bez přidání připojovací řetězec do historie shellu.

Open Developer PowerShell pro VS 2022. Spusťte tyto příkazy a potom do zobrazené výzvy vložte připojovací řetězec:

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

Proměnná prostředí uchovává připojovací řetězec mimo zdrojový soubor, ale proces a jeho podprocesy jej mohou číst. Pro produkční aplikace používejte Microsoft Entra autentizaci, kde je to možné, a během běhu získávejte tajemství ze zabezpečeného úložiště.

Vytvoření aplikace

  1. Vytvořte adresář projektu a přejděte do něj:

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

  1. Vytvořte soubor s názvem odbc-quickstart.cpp s následujícím kódem:

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

Aplikace používá pouze standardní ODBC API, takže obsahuje hlavičky správce ovladačů a odkazy na knihovnu správce ovladačů. Připojovací řetězec za běhu vybírá Microsoft ODBC Driver 18 for SQL Server.

SQL_DRIVER_NOPROMPT brání SQLDriverConnect otevření konfiguračního dialogu. Pokud je připojovací řetězec neúplný, volání vrátí chybu a aplikace vytiskne všechny diagnostické záznamy.

Dotaz naváže 0 jako parametr typu SQL Server int, přečte prvních pět produktů v SalesLT.Product a pomocí SQLGetData načte ID a název každého produktu.

Sestavení a spuštění aplikace

  1. Ve stejném okně Developer PowerShell zkompilujte aplikaci:

    cl /std:c++17 /EHsc /W4 odbc-quickstart.cpp /link odbc32.lib
    
  2. Spusťte aplikaci:

    .\odbc-quickstart.exe
    

Standardní výstup aplikace je:

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

Po dokončení připojovací řetězec vymažte.

Remove-Item Env:\ODBC_CONNECTION_STRING