AddOdbcParamter

Add an ODBC connection-string parameter to a DATE_LOG_HANDLE's connection slot

Overview

AddOdbcParamter validates and adds a single connection-string parameter (keyword/value pair, tri-state flag, or protected credential) into the internal parameter list of the ODBC connection identified by OdbcLevel on hDateLogHandle. It is the public, handle-based wrapper around OdbcAddParamImpl(), resolving OdbcLevel to its PODBC_CONNECTION_CONTEXT via GetOdbcConnectionContextFromHandle() under the handle’s lock. (The function name is spelled “Paramter” in the library; use that spelling exactly.)

Purpose

This is the function that the public ODBC_CONNECT_PARAMS page describes as the target of the AddParam function pointer; however it may also be called directly against an already-initialized ODBC connection slot on a DATE_LOG_HANDLE (see InitializeDateLogHandleOdbc).

Key Features

Function Signature


HRESULT _Must_inspect_result_ _Success_(SUCCEEDED(return)) AddOdbcParamter(
    _In_     DATE_LOG_HANDLE     hDateLogHandle,
    _In_     ODBC_PARAM_KEY      eKey,
    _In_opt_ LPVOID              pvValue,
    _In_     DWORD               cbValue,
    _In_     ODBC_PARAM_TYPE     eType,
    _In_     ODBC_TRISTATE       eTriValue,
    _In_     DWORD               dwCredFlags,
    _In_     DATE_LOG_ODBC_LEVEL OdbcLevel
);

Parameters

hDateLogHandle

[In] DATE_LOG_HANDLE

A valid DATE_LOG_HANDLE with ODBC connection internals already initialized (see InitializeDateLogHandleOdbc).

  • NULL: not allowed (HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE))
  • Ownership: unchanged; the handle is not closed or consumed
  • Lifetime: must remain valid for the duration of the call; the added parameter belongs to the handle’s connection and is released with it by CloseDateLogHandle
eKey

[In] ODBC_PARAM_KEY

The enumerator identifying the connection-string keyword (for example ODBC_KEY_SERVER or ODBC_KEY_DRIVER).

  • Must form a valid combination with eType, eTriValue, and dwCredFlags; otherwise HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)
pvValue

[In, Optional] LPVOID

The value to encode.

  • NULL: allowed only for ODBC_PARAM_TYPE_TRISTATE; required for every other type
  • Ownership: the caller keeps ownership of the buffer; it is only read during the call, so it may be freed or reused afterwards
  • Meaning: string/raw data, or a protected credential buffer, depending on eType
cbValue

[In] DWORD

Size in bytes of pvValue (string/raw/protected-credential types).

  • Ignored in practice for ODBC_PARAM_TYPE_TRISTATE, which carries no value buffer
eType

[In] ODBC_PARAM_TYPE

How the value is interpreted.

  • ODBC_PARAM_TYPE_STRING — text value
  • ODBC_PARAM_TYPE_RAW — raw bytes
  • ODBC_PARAM_TYPE_TRISTATE — flag taken from eTriValue; pvValue may be NULL
  • ODBC_PARAM_TYPE_PROTECTED_CREDENTIAL — protected credential; dwCredFlags applies
eTriValue

[In] ODBC_TRISTATE

ODBC_YES, ODBC_NO, or ODBC_UNSPECIFIED; meaningful only for ODBC_PARAM_TYPE_TRISTATE.

  • Pass ODBC_UNSPECIFIED for all other types
dwCredFlags

[In] DWORD

CRED_PACK_* flags originally passed to CredPackAuthenticationBufferW(); meaningful only for ODBC_PARAM_TYPE_PROTECTED_CREDENTIAL.

  • Pass 0 for all other types
OdbcLevel

[In] DATE_LOG_ODBC_LEVEL

DATE_LOG_ODBC_1 through DATE_LOG_ODBC_4, identifying which of up to four connection slots to target (DATE_LOG_MAX_ODBC_CONNECTIONS).

Return Values

S_OK

Parameter added successfully.

ErrorCause
HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE)hDateLogHandle is NULL.
HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)Key/type combination invalid (IsOdbcParamSetOk() failed), or pvValue is NULL for a non-tristate type.
HRESULT_FROM_WIN32(ERROR_INVALID_FLAGS)OdbcLevel is 0, exceeds DATE_LOG_MAX_ODBC_CONNECTIONS, or no connection exists yet at that level.
HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE_STATE)The handle’s ODBC connection internals have not been initialized.
HRESULT_FROM_WIN32(ERROR_UNIDENTIFIED_ERROR)No connections have been registered on this handle yet (lParamCount == 0).

Always test the result with FAILED()/SUCCEEDED(); the function is marked _Must_inspect_result_.

Behavior

Validate key/type/flags
→
Lock handle
→
Resolve connection for OdbcLevel
→
OdbcAddParamImpl()
→
Unlock

Validates the key/type/tri-state/cred-flags combination via IsOdbcParamSetOk(), resolves the target connection context for OdbcLevel, and delegates the actual SLIST insertion (including singleton-key replacement semantics for keys such as SERVER/DRIVER/DSN/UID/PWD) to OdbcAddParamImpl(). The entire operation is performed under the DATE_LOG_HANDLE’s validated lock.

Remarks

Thread Safety

The operation runs under the handle’s validated lock, so concurrent calls on one handle are serialized. Duplicates created with DuplicateDateLogHandle share the same connections and therefore the same parameter lists.

Handle Lifetime and Ownership

The parameter is stored on the connection at OdbcLevel; the connection’s parameter list is released together with the connection when the last handle referencing the context is closed via CloseDateLogHandle. Your value buffer is not retained.

Ordering

Documentation Notes

Sample Code

The example creates a date log, reserves ODBC slot 1, adds the driver and server (replacing the server once to show singleton-key behavior), and closes all handles on every path.


#include <stdio.h>
#include <wchar.h>
#include "datelog.h"


static HRESULT BuildSampleDateLog(HOLIDAYS_HANDLE hHolidays, DATE_LOG_HANDLE *phDateLog)
{
    DATE_LOG_HANDLE  hDateLog = NULL;
    DATE_ITEM_HANDLE hStart   = NULL;
    DATE_ITEM_HANDLE hEnd     = NULL;
    SYSTEMTIME       stStart  = { 0 };
    SYSTEMTIME       stEnd    = { 0 };
    FILETIME         ftStart  = { 0 };
    FILETIME         ftEnd    = { 0 };
    HRESULT          hr       = E_FAIL;

    *phDateLog = NULL;

    /* Tuesday 2 January 2024 through Wednesday 31 January 2024 (both market days). */
    stStart.wYear = 2024; stStart.wMonth = 1; stStart.wDay = 2;
    stEnd.wYear   = 2024; stEnd.wMonth   = 1; stEnd.wDay   = 31;
    if (!SystemTimeToFileTime(&stStart, &ftStart) || !SystemTimeToFileTime(&stEnd, &ftEnd)) {
        return HRESULT_FROM_WIN32(GetLastError());
    }

    hr = InitializeDateLogHandle(NULL, DATE_LOG_MAX, hHolidays, &hDateLog, HandleIsForDatelog);
    if (FAILED(hr)) {
        return hr;
    }

    hr = InitializeDateItem(hDateLog, &ftStart, NULL, &hStart);
    if (SUCCEEDED(hr)) {
        hr = InitializeDateItem(hDateLog, &ftEnd, NULL, &hEnd);
    }
    if (SUCCEEDED(hr)) {
        /* No file context: generate the contiguous range between hStart and hEnd. */
        hr = LoadDateLogHandle(NULL, hStart, hEnd, hDateLog, NULL, 0);
    }

    FreeDateItem(hEnd);
    FreeDateItem(hStart);

    if (FAILED(hr)) {
        CloseDateLogHandle(hDateLog);
        return hr;
    }

    *phDateLog = hDateLog;
    return S_OK;
}

int main(void)
{
    HOLIDAYS_HANDLE hHolidays = NULL;
    DATE_LOG_HANDLE hDateLog  = NULL;
    HRESULT         hr        = E_FAIL;
    int             rc        = 1;

    /* Value strings. cbValue is a size in bytes; see Remarks for encoding notes. */
    static const WCHAR szDriver[] = L"{ODBC Driver 17 for SQL Server}";
    static const WCHAR szServer[] = L"localhost\\SQLEXPRESS";

    /* Holidays handle. Populate it with your market holiday data (see
       LoadDateLogHandle) before building date logs; that step is application specific. */
    hr = InitializeDateLogHandle(NULL, DATE_LOG_MAX, NULL, &hHolidays, HandleIsForHolidays);
    if (FAILED(hr)) {
        printf("InitializeDateLogHandle failed for holidays: 0x%08lX\n", (unsigned long)hr);
        return 1;
    }

    hr = BuildSampleDateLog(hHolidays, &hDateLog);
    if (FAILED(hr)) {
        printf("BuildSampleDateLog failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    /* The connection slot must be reserved before parameters can be added to it. */
    hr = InitializeDateLogHandleOdbc(hDateLog, DATE_LOG_ODBC_1);
    if (FAILED(hr)) {
        printf("InitializeDateLogHandleOdbc failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    hr = AddOdbcParamter(hDateLog, ODBC_KEY_DRIVER, (LPVOID)szDriver,
                         (DWORD)(wcslen(szDriver) * sizeof(WCHAR)),
                         ODBC_PARAM_TYPE_STRING, ODBC_UNSPECIFIED, 0, DATE_LOG_ODBC_1);
    if (FAILED(hr)) {
        printf("Adding the driver failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    hr = AddOdbcParamter(hDateLog, ODBC_KEY_SERVER, (LPVOID)szServer,
                         (DWORD)(wcslen(szServer) * sizeof(WCHAR)),
                         ODBC_PARAM_TYPE_STRING, ODBC_UNSPECIFIED, 0, DATE_LOG_ODBC_1);
    if (FAILED(hr)) {
        printf("Adding the server failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    /* Server is a singleton key: adding it again replaces the earlier value. */
    hr = AddOdbcParamter(hDateLog, ODBC_KEY_SERVER, (LPVOID)L"dbhost",
                         (DWORD)(wcslen(L"dbhost") * sizeof(WCHAR)),
                         ODBC_PARAM_TYPE_STRING, ODBC_UNSPECIFIED, 0, DATE_LOG_ODBC_1);
    if (FAILED(hr)) {
        printf("Replacing the server failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    printf("Connection parameters were added to ODBC level 1.\n");
    rc = 0;

cleanup:
    /* Closing the handle also releases the parameter list of its connections. */
    if (hDateLog != NULL && FAILED(CloseDateLogHandle(hDateLog))) {
        rc = 1;
    }
    if (FAILED(CloseDateLogHandle(hHolidays))) {
        rc = 1;
    }
    return rc;
}

See Also