Hızlı Başlangıç: C++ ve ODBC ile bağlantı ve sorgulama

Şunlar için geçerlidir:SQL ServerAzure SQL Veritabanı

Bu hızlı başlangıçta, Windows, Linux veya macOS üzerinde bir C++ konsol uygulaması oluşturuyorsunuz. Uygulama, Microsoft ODBC Driver 18 for SQL Server kullanarak veritabanına AdventureWorksLT bağlanır, bir sorgu parametresi bağlar, sorguyu çalıştırır ve sonuç satırlarını okur.

Prerequisites

  • Microsoft ODBC Driver 18 for SQL Server. Sürücüyü Windows, Linux veya macOS için yükleyin.
  • Bir C++17 derleyicisi ve platformun ODBC geliştirme dosyaları:
    • Windows'ta, C++ ile Masaüstü Geliştirme iş yüküyle Visual Studio 2022'yi veya Build Tools for Visual Studio 2022'yi yükleyin. Windows SDK, ODBC üst bilgilerini ve odbc32.lib sağlar.
    • Linux'ta, dağıtımınız için C++ derleyici ve unixODBC geliştirme paketini yükleyin. Paket, ODBC başlık dosyalarını ve libodbc sağlar.
    • macOS'ta, Xcode komut satırı araçlarını ve Homebrew'den unixODBC'yi yükleyin.
  • Azure SQL Veritabanı veya SQL Server'da örnek verileri içeren AdventureWorksLT bir veritabanı. Azure SQL Veritabanı için, tek bir veritabanı oluşturduğunuzdaÖrnek veri kaynağını seçin. SQL Server için, AdventureWorks örnek veritabanlarından bir AdventureWorksLT yedeklemeyi geri getirin.

Sürücüyü doğrulayın

Sürücü yöneticisinin SQL Server için Microsoft ODBC Driver 18 bulabildiğini doğrulayın.

PowerShell'de bu komutu çalıştırın:

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

Her komut, ODBC Driver 18 for SQL Server ögesini listelemelidir. Sürücü listede yoksa, devam etmeden önce tekrar yükleyin.

Bağlantıyı yapılandırma

Uygulama, ortam değişkeninden tam bağlantı dizesi'i ODBC_CONNECTION_STRING okur. Bu cihaz bağlantı dizesi'i göstermez veya komut satırı argümanı olarak kabul etmez.

Veritabanınız ve kimlik doğrulama yönteminiz için geçerli olan bir Driver 18 bağlantı dizesi kullanın. Aşağıdaki SQL kimlik doğrulama örneği, veritabanı SQL kimlik doğrulamasına izin verdiğinde Windows, Linux ve macOS'ta çalışır:

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

Microsoft Entra kimlik doğrulama seçenekleri için, ODBC sürücüsüyle Microsoft Entra ID'sini Kullan'a bakınız. Tüm desteklenen ayarlar için DSN ve bağlantı dizesi anahtar kelimeleri ve niteliklerine bakınız.

Microsoft ODBC Sürücüsü 18, varsayılan olarak şifrelemeyi etkinleştirir. Uygulamanın gereksiniminin açık olması için belirtin Encrypt=yes . Sürücünün sunucu sertifikasını doğrulaması için TrustServerCertificate=no öğesini üretimde tutun. Sertifika, sunucu adını ve zincirini istemcinin güvendiği bir sertifikasyon otoritesine eşleştirmelidir. Yapılandırma rehberliği için bkz. Sertifika doğrulama hataları.

Caution

TrustServerCertificate=yes sertifika doğrulamasını atlar. Sadece izole yerel geliştirme için kullanın, böylece istemcinin güvendiği bir sertifikayı yapılandırırsınız. Üretimde kullanmayın.

Çevre değişkenini shell geçmişinize bağlantı dizesi eklemeden ayarlayın.

VS 2022 için Geliştirici PowerShell'i aç. Bu komutları çalıştırın ve ardından bağlantı dizesi'i prompta yapıştırın:

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

Bir ortam değişkeni, bağlantı dizesi'i kaynak dosyadan uzak tutar, ancak süreç ve alt süreçleri onu okuyabilir. Üretim uygulamaları için, mümkün olduğunda Microsoft Entra doğrulamasını kullanın ve çalışma zamanında güvenli bir depodan sırları alın.

Uygulamayı oluşturma

  1. Bir proje dizini oluşturun ve ona dönüştürün:

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

  1. Aşağıdaki kodla adlı odbc-quickstart.cpp bir dosya oluşturun:

    #ifdef _WIN32
    #include <windows.h>
    #endif
    
    #include <sql.h>
    #include <sqlext.h>
    #include <sqltypes.h>
    
    #include <cstdlib>
    #include <iomanip>
    #include <iostream>
    #include <string>
    #include <string_view>
    
    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
    }
    
    bool PrintDiagnostics(SQLSMALLINT handleType, SQLHANDLE handle)
    {
        bool dataTruncated = false;
        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';
            if (std::string_view(reinterpret_cast<const char*>(state), 5) == "01004")
                dataTruncated = true;
        }
        return dataTruncated;
    }
    
    bool Succeeded(SQLRETURN result, SQLSMALLINT handleType, SQLHANDLE handle,
                   bool* dataTruncated = nullptr)
    {
        if (result == SQL_SUCCESS)
            return true;
    
        const bool diagnosticReportsTruncation = PrintDiagnostics(handleType, handle);
        if (dataTruncated != nullptr)
            *dataTruncated = diagnosticReportsTruncation;
        return result == SQL_SUCCESS_WITH_INFO;
    }
    
    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 (!Succeeded(result, SQL_HANDLE_ENV, handles.environment))
        {
            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 ((result = SQLFetch(handles.statement)) != SQL_NO_DATA)
        {
            if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement))
                return 1;
    
            SQLINTEGER productId = 0;
            SQLLEN productIdLength = 0;
            SQLCHAR productName[256] = {};
            SQLLEN productNameLength = 0;
    
            result = SQLGetData(
                handles.statement, 1, SQL_C_SLONG, &productId,
                sizeof(productId), &productIdLength);
            if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement))
                return 1;
    
            bool productNameTruncated = false;
            result = SQLGetData(
                handles.statement, 2, SQL_C_CHAR, productName,
                sizeof(productName), &productNameLength);
            if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement,
                           &productNameTruncated))
                return 1;
    
            std::string productNameText;
            if (productNameLength == SQL_NULL_DATA)
            {
                productNameText = "<NULL>";
            }
            else if (productNameTruncated ||
                     productNameLength > static_cast<SQLLEN>(sizeof(productName) - 1))
            {
                std::cerr << "Product name was truncated; no partial value was printed.\n";
                return 1;
            }
            else if (productNameLength == SQL_NO_TOTAL)
            {
                std::cerr << "Product name length is unknown; no value was printed.\n";
                return 1;
            }
            else if (productNameLength < 0)
            {
                std::cerr << "The driver returned an invalid product-name length.\n";
                return 1;
            }
            else if (productNameLength == 0)
            {
                productNameText = "<empty>";
            }
            else
            {
                productNameText.assign(
                    reinterpret_cast<const char*>(productName),
                    static_cast<std::size_t>(productNameLength));
            }
    
            std::cout << std::left << std::setw(12) << productId
                      << productNameText << '\n';
        }
    
        return 0;
    }
    

Uygulama yalnızca standart ODBC API'sini kullanır, bu nedenle sürücü-yönetici başlıkları ve sürücü-yönetici kütüphanesine bağlantılar içerir. bağlantı dizesi, çalışma zamanında SQL Server için Microsoft ODBC Driver 18'i seçer.

SQL_DRIVER_NOPROMPT yapılandırma diyalogu açılmasını engeller SQLDriverConnect . Bağlantı dizesi eksikse, çağrı bir hata döndürür ve uygulama her tanılama kaydını yazdırır. Uygulama ayrıca, denetlenen bir ODBC çağrısı SQL_SUCCESS_WITH_INFO döndürdüğünde tanılama bilgilerini yazdırır; bu, çağrının bir uyarıyla başarılı olduğu anlamına gelir.

Sorgu, öğesini SQL Server 0 parametresi olarak bağlar, SalesLT.Product içindeki ilk beş ürünü okur ve SQLGetData ile her ürünün kimliğini ve adını alır. Bir ürün adı için uygulama, <empty> için SQL_NULL_DATA ve sıfır uzunluklu bir değer için <NULL> görüntüler. SQLGetData SQLSTATE 01004, bilinmeyen bir uzunluk veya tampondan büyük bir uzunluk bildirirse, uygulama sorunu bildirir ve kısmi bir isim yazmadan çıkış yapar.

Uygulamayı derleme ve çalıştırma

  1. Aynı Geliştirici PowerShell penceresinde uygulamayı derleyin:

    cl /std:c++17 /EHsc /W4 odbc-quickstart.cpp /link odbc32.lib
    
  2. Uygulamayı çalıştırın:

    .\odbc-quickstart.exe
    

Uygulamanın standart çıktısı şudur:

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

İşi bitirince bağlantı dizesi'i temizleyin.

Remove-Item Env:\ODBC_CONNECTION_STRING