クイックスタート:C++とODBCで接続・クエリ

適用対象:SQL ServerAzure SQL データベース

このクイックスタートでは、Windows、Linux、macOS上でC++コンソールアプリケーションを構築します。 アプリケーションは、Microsoft ODBC Driver 18 for SQL Server を使用して AdventureWorksLT データベースに接続し、クエリ パラメーターをバインドし、クエリを実行し、結果行を読み取ります。

前提条件

  • Microsoft ODBC Driver 18 for SQL Server. WindowsLinuxmacOS用のドライバーをインストールしてください。
  • C++17コンパイラとプラットフォームのODBC開発ファイル:
    • Windowsでは、Visual Studio 2022またはデスクトップ開発(C++対応)ワークロードでVisual Studio 2022のビルドツールをインストールしてください。 Windows SDKはODBCヘッダーとodbc32.libを提供します。
    • Linuxでは、C++コンパイラとunixODBC開発パッケージをインストールしてください。 パッケージはODBCヘッダーと libodbcを供給します。
    • macOSでは、XcodeコマンドラインツールとHomebrewからunixODBCをインストールしてください。
  • Azure SQL DatabaseまたはSQL Serverのデータベースで、AdventureWorksLTサンプルデータを含むものです。 Azure SQL Databaseの場合は、単一のデータベースを作成する際にサンプルデータソースを選択してください。 SQL Serverは、AdventureWorksのサンプルデータベースからAdventureWorksLTバックアップを復元してください。

運転手の確認を

ドライバーマネージャーでMicrosoft ODBC Driver 18 for SQL Serverが見つかるか確認してください。

PowerShellで以下のコマンドを実行します:

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

各コマンドは ODBC Driver 18 for SQL Serverをリストにすべきです。 ドライバーがリストにない場合は、先にインストールしてから続けてください。

接続の設定

アプリケーションはODBC_CONNECTION_STRING環境変数から完全な接続文字列を読み込みます。 接続文字列を表示せず、コマンドラインの引数として受け入れられません。

データベースと認証方法に有効なDriver 18の接続文字列を使いましょう。 以下のSQL認証の例は、データベースがSQL認証を許可している場合、Windows、Linux、macOSで動作します。

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

Microsoft Entraの認証オプションについては、「ODBCドライバーでMicrosoft Entra IDを使用する」をご覧ください。 すべての対応設定については、DSNおよび接続文字列キーワードおよび属性を参照してください。

Microsoft ODBC Driver 18はデフォルトで暗号化を有効にしています。 Encrypt=yes を指定して、アプリケーションの要件を明示的にしてください。 TrustServerCertificate=no本番環境で保持して、ドライバーがサーバー証明書を検証します。 証明書はサーバー名とチェーンをクライアントが信頼する認証機関と一致しなければなりません。 設定ガイダンスについては、 証明書検証失敗を参照してください。

Caution

TrustServerCertificate=yes 証明書の検証を省略します。 クライアントが信頼する証明書を設定する間、ローカル開発の孤立した用途にのみ使ってください。 運用環境では使用しないでください。

環境変数は、接続文字列をシェル履歴に追加せずに設定してください。

VS 2022用のOpen Developer PowerShell。 これらのコマンドを実行し、プロンプトの接続文字列を貼り付けてください:

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

環境変数は接続文字列をソースファイルから除外しますが、プロセスとその子プロセスはそれを読み取ることができます。 本番アプリケーションでは可能な限りMicrosoft Entra認証を使用し、実行時に安全なストアから秘密を取得します。

アプリケーションを作成する

  1. プロジェクトディレクトリを作成し、それに変更してください:

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

  1. 次のコードを使用して、 odbc-quickstart.cpp という名前のファイルを作成します。

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

アプリケーションは標準のODBC APIのみを使用しているため、ドライバーマネージャーのヘッダーとドライバーマネージャーライブラリへのリンクが含まれています。 接続文字列は実行時にMicrosoft ODBCドライバー18 for SQL Serverを選択します。

SQL_DRIVER_NOPROMPT SQLDriverConnect設定ダイアログを開くことを阻止します。 接続文字列が不完全の場合、呼び出しはエラーを返し、アプリケーションはすべての診断レコードを印刷します。

クエリは0をSQL Server intパラメータとしてバインドし、最初の5つの製品をSalesLT.Productで読み取り、各製品IDと名前をSQLGetDataで取得します。

アプリケーションの構築と実行

  1. 同じ開発者PowerShellウィンドウでアプリケーションをコンパイルします:

    cl /std:c++17 /EHsc /W4 odbc-quickstart.cpp /link odbc32.lib
    
  2. アプリケーションを実行します。

    .\odbc-quickstart.exe
    

アプリケーションの標準出力は以下の通りです:

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

完了したら、接続文字列をクリアしてください。

Remove-Item Env:\ODBC_CONNECTION_STRING