Inicio rápido: Conecta y consulta con C++ y ODBC

Se aplica a:SQL ServerAzure SQL Database

En este inicio rápido, construyes una aplicación de consola C++ en Windows, Linux o macOS. La aplicación se conecta a una AdventureWorksLT base de datos utilizando el controlador ODBC 18 de Microsoft para SQL Server, vincula un parámetro de consulta, ejecuta la consulta y lee las filas de resultados.

Prerequisites

  • Microsoft ODBC Driver 18 para SQL Server. Instala el controlador para Windows, Linux o macOS.
  • Un compilador C++17 y los archivos de desarrollo ODBC de la plataforma:
    • En Windows, instala Visual Studio 2022 o las Herramientas de Compilación para Visual Studio 2022 con la carga de trabajo de desarrollo de escritorio con C++. El SDK de Windows proporciona los encabezados ODBC y odbc32.lib.
    • En Linux, instala un compilador en C++ y el paquete de desarrollo unixODBC para tu distribución. El paquete suministra los encabezados ODBC y libodbc.
    • En macOS, instala las herramientas de línea de comandos de Xcode y unixODBC desde Homebrew.
  • Una base de datos en Azure SQL Database o SQL Server que contiene los AdventureWorksLT datos de ejemplo. Para Azure SQL Database, selecciona la fuente de datos de ejemplo cuando crees una única base de datos. Para SQL Server, restaura una AdventureWorksLT copia de seguridad desde bases de datos de ejemplo de AdventureWorks.

Verifica el controlador

Confirma que el gestor de controladores puede encontrar el controlador ODBC 18 de Microsoft para SQL Server.

Ejecuta este comando en PowerShell:

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

Cada comando debería indicar ODBC Driver 18 for SQL Server. Si el controlador no aparece en la lista, reinstálalo antes de continuar.

Configurar la conexión

La aplicación lee la cadena de conexión completa de la variable de entorno ODBC_CONNECTION_STRING. No muestra la cadena de conexión ni la acepta como argumento de línea de comandos.

Usa una cadena de conexión del Driver 18 que sea válida para tu base de datos y método de autenticación. El siguiente ejemplo de autenticación SQL funciona en Windows, Linux y macOS cuando la base de datos permite la autenticación SQL:

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

Para las opciones de autenticación de Microsoft Entra, consulte Usar Microsoft Entra ID con el controlador ODBC. Para todas las opciones de configuración admitidas, consulta DSN, palabras clave y atributos de la cadena de conexión.

El controlador ODBC 18 de Microsoft habilita el cifrado por defecto. Especifica Encrypt=yes para que el requisito de la solicitud sea explícito. Mantenlo TrustServerCertificate=no en producción para que el controlador valide el certificado del servidor. El certificado debe coincidir el nombre del servidor y la cadena con una autoridad de certificación en la que el cliente confíe. Para orientación de configuración, véase Fallas de validación de certificados.

Caution

TrustServerCertificate=yes Salta la validación de certificados. Úsalo solo para desarrollo local aislado mientras configuras un certificado en el que el cliente confíe. No lo use en producción.

Establece la variable de entorno sin añadir la cadena de conexión al historial de tu shell.

OpenDeveloper PowerShell para VS 2022. Ejecuta estos comandos y luego pega la cadena de conexión en el prompt:

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

Una variable de entorno mantiene la cadena de conexión fuera del archivo de código fuente, pero el proceso y sus procesos hijos pueden leerla. Para aplicaciones de producción, utiliza la autenticación de Microsoft Entra siempre que sea posible y recupera secretos de un almacén seguro en tiempo de ejecución.

Creación de la aplicación

  1. Crea un directorio de proyectos y cámbialo a él:

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

  1. Cree un archivo denominado odbc-quickstart.cpp con el código siguiente:

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

La aplicación utiliza únicamente la API estándar ODBC, por lo que incluye los encabezados de driver-manager y enlaces a la biblioteca driver-manager. La cadena de conexión selecciona el controlador ODBC 18 de Microsoft para SQL Server en tiempo de ejecución.

SQL_DRIVER_NOPROMPT impide SQLDriverConnect abrir un cuadro de diálogo de configuración. Si la cadena de conexión está incompleta, la llamada devuelve un error y la aplicación imprime todos los registros diagnósticos.

La consulta enlaza 0 como un parámetro int de SQL Server, lee los primeros cinco productos de SalesLT.Product y recupera el identificador y el nombre de cada producto con SQLGetData.

Compilación y ejecución de la aplicación

  1. En la misma ventana de PowerShell para desarrolladores, compila la aplicación:

    cl /std:c++17 /EHsc /W4 odbc-quickstart.cpp /link odbc32.lib
    
  2. Ejecute la aplicación:

    .\odbc-quickstart.exe
    

La salida estándar de la aplicación es:

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

Borra la cadena de conexión cuando termines.

Remove-Item Env:\ODBC_CONNECTION_STRING