FetchDateLogHandleToOdbcDatabase

Push the date log core (and optionally holidays) into an ODBC backend table

Overview

FetchDateLogHandleToOdbcDatabase is the high-level orchestrator that resolves the ODBC_CONNECTION_CONTEXT for OdbcLevel from hDateLogHandle's existing connection, optionally creates the destination table(s) described by pBulk (and pBulk->pHolidaysBulk, if present), reconciles an existing table against the current core if needed, and bulk-inserts the date log collection(s).

Purpose

Despite the name, the data flows from the in-memory date log to the ODBC database: the handle's core is written into the table described by pBulk. The call is a single entry point for the whole "make the backend table match the date log" workflow, so callers do not build plans, create tables, or issue inserts individually.

Key Features

Function Signature


HRESULT _Must_inspect_result_ _Success_(SUCCEEDED(return)) FetchDateLogHandleToOdbcDatabase(
    _In_    DATE_LOG_HANDLE      hDateLogHandle,
    _In_    DATE_LOG_ODBC_LEVEL  OdbcLevel,
    _Inout_ PSQL_DATE_BULK       pBulk,
    _In_    DWORD                dwFlags   // reserved, pass 0
);

Parameters

hDateLogHandle

[In] DATE_LOG_HANDLE

The date log whose core is written to the database.

  • NULL: not allowed (ERROR_INVALID_PARAMETER).
  • Valid values: a DATE_LOG_MAGIC handle (a date log, not a range or holidays handle) that already has a loaded core and an ODBC connection at OdbcLevel, created with InitializeDateLogHandleOdbc() and ConnectDateLogHandleToOdbcDatabase().
  • Ownership/lifetime: the caller keeps ownership. The ODBC connection stays owned by the handle and is closed by CloseDateLogHandle().
OdbcLevel

[In] DATE_LOG_ODBC_LEVEL

Which of the handle's connection slots to use.

  • Valid values: DATE_LOG_ODBC_1 through DATE_LOG_ODBC_4 (at most DATE_LOG_MAX_ODBC_CONNECTIONS = 4 connections per handle); the slot must already be connected.
  • Invalid values: an out-of-range level yields ERROR_INVALID_PARAMETER.
pBulk

[In, Out] PSQL_DATE_BULK

Describes the destination table (SQL_TABLE_SPECS or SQL_TABLE_QUERY) and optionally pBulk->pHolidaysBulk for a second, independent holidays table.

  • NULL: not allowed (ERROR_INVALID_PARAMETER).
  • Valid values: a descriptor that passes IsValidOdbcBulk(); otherwise ERROR_INVALID_PARAMETER is returned. If pHolidaysBulk is present, the handle must have a holidays core.
  • Ownership/lifetime: caller-owned and updated by the function (_Inout_); it must stay valid for the whole call. See SQL_DATE_BULK for the structure and its documented members.
dwFlags

[In] DWORD

Reserved for future use.

  • Valid values: pass 0.

Return Values

Check the result with FAILED/SUCCEEDED. The codes below are the ones documented for this function; failures from the helpers it calls are propagated unchanged.

Return valueMeaning
S_OKData fetched/inserted successfully.
HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)NULL arguments, invalid OdbcLevel, or pBulk fails IsValidOdbcBulk().
HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE_STATE)No connection internals, no core, or pBulk->pHolidaysBulk present but holidays core missing.
HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE)Connection context or plan could not be resolved.
Other failure HRESULTPropagated from BuildOdbcDateColumnPlan, CreateOdbcDateLogTable, BulkInsertDateLogCoreToOdbc, IsOdbcTableExisting, IsOdbcTableColumnLayoutMatchingPlan, ReconcileOdbcDateColumnOrderFromTable, or ReconcileOdbcDateLogTableFetchPlan.

Behavior and Operations

Resolve Core + Connection
→
Find/Build Plan(s)
→
Create or Reconcile Table(s)
→
Bulk Insert
→
Commit/Rollback
If the table already exists and its layout doesn't match the plan, the column order is reconciled from SQLColumns() before computing which row ranges (before/after segments) still need inserting.

Remarks

Documentation inconsistencies: the SQL_DATE_BULK page lists only RecordCount, MaxRecordCount, Records, RowStatusArray, and LastOperationStatus, and shows a three-argument "fetch from database" call; it does not document SQL_TABLE_SPECS, SQL_TABLE_QUERY, or pHolidaysBulk. This page keeps the four-argument signature above, which is not declared in datelog.h (the header exposes SetDateLogHandleOdbcBulkInsertPort instead).

See Also

Sample Code


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

static BOOL MakeFiletime(WORD wYear, WORD wMonth, WORD wDay, PFILETIME pFiletime)
{
    SYSTEMTIME st = { 0 };

    st.wYear = wYear;
    st.wMonth = wMonth;
    st.wDay = wDay;
    return SystemTimeToFileTime(&st, pFiletime);
}

int main(void)
{
    HOLIDAYS_HANDLE  hHolidays = NULL;
    DATE_LOG_HANDLE  hDateLog = NULL;
    DATE_ITEM_HANDLE hStart = NULL;
    DATE_ITEM_HANDLE hEnd = NULL;
    FILETIME         ftStart = { 0 };
    FILETIME         ftEnd = { 0 };
    HRESULT          hr = E_FAIL;
    int              exitCode = 1;
    PODBC_CONNECT_PARAMS pParams = NULL;
    SQL_DATE_BULK    bulk = { 0 };

    hr = InitializeDateLogHandle(NULL, DATE_LOG_MAX, NULL, &hHolidays, HandleIsForHolidays);
    if (FAILED(hr)) {
        printf("InitializeDateLogHandle [holidays] failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    hr = InitializeDateLogHandle(NULL, DATE_LOG_MAX, hHolidays, &hDateLog, HandleIsForDatelog);
    if (FAILED(hr)) {
        printf("InitializeDateLogHandle [date log] failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    if (!MakeFiletime(2026, 1, 5, &ftStart) || !MakeFiletime(2026, 3, 31, &ftEnd)) {
        printf("SystemTimeToFileTime failed: %lu\n", GetLastError());
        goto cleanup;
    }

    /* Standalone date items; the caller owns them and frees them with FreeDateItem. */
    hr = InitializeDateItem(hHolidays, &ftStart, NULL, &hStart);
    if (FAILED(hr)) {
        printf("InitializeDateItem [start] failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    hr = InitializeDateItem(hHolidays, &ftEnd, NULL, &hEnd);
    if (FAILED(hr)) {
        printf("InitializeDateItem [end] failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    /* The holidays collection is populated first, then the date log over the same range. */
    hr = LoadDateLogHandle(NULL, hStart, hEnd, hHolidays, NULL, 0);
    if (FAILED(hr)) {
        printf("LoadDateLogHandle [holidays] failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    hr = LoadDateLogHandle(NULL, hStart, hEnd, hDateLog, NULL, 0);
    if (FAILED(hr)) {
        printf("LoadDateLogHandle [date log] failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    /* Reserve connection slot 1 on the date log, then connect it. */
    hr = InitializeDateLogHandleOdbc(hDateLog, DATE_LOG_ODBC_1);
    if (FAILED(hr)) {
        printf("InitializeDateLogHandleOdbc failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

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

    /* Parameters are added through the AddParam function pointer on the instance. */
    hr = pParams->AddParam(pParams, "Driver", "{ODBC Driver 17 for SQL Server}");
    if (FAILED(hr)) {
        printf("AddParam [Driver] failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }
    hr = pParams->AddParam(pParams, "Server", "localhost\\SQLEXPRESS");
    if (FAILED(hr)) {
        printf("AddParam [Server] failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }
    hr = pParams->AddParam(pParams, "Database", "DateDatabase");
    if (FAILED(hr)) {
        printf("AddParam [Database] failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }
    hr = pParams->AddParam(pParams, "Trusted_Connection", "yes");
    if (FAILED(hr)) {
        printf("AddParam [Trusted_Connection] failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    /* The handle owns the connection from here on; pParams is not retained. */
    hr = ConnectDateLogHandleToOdbcDatabase(hDateLog, pParams, DATE_LOG_ODBC_1);
    if (FAILED(hr)) {
        printf("ConnectDateLogHandleToOdbcDatabase failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    hr = FreeOdbcConnectParams(pParams);
    pParams = NULL;
    if (FAILED(hr)) {
        printf("FreeOdbcConnectParams failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    /*
     * bulk describes the destination table (and, optionally, a separate holidays table through
     * pHolidaysBulk). Populate it according to your SQL_DATE_BULK definition before the call;
     * a descriptor that fails IsValidOdbcBulk() is rejected with ERROR_INVALID_PARAMETER.
     */
    hr = FetchDateLogHandleToOdbcDatabase(hDateLog, DATE_LOG_ODBC_1, &bulk, 0);
    if (hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)) {
        printf("The bulk descriptor or OdbcLevel is invalid.\n");
        goto cleanup;
    }
    if (hr == HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE_STATE)) {
        printf("No connection internals / core (or holidays core) available.\n");
        goto cleanup;
    }
    if (FAILED(hr)) {
        printf("FetchDateLogHandleToOdbcDatabase failed: 0x%08lX\n", (unsigned long)hr);
        goto cleanup;
    }

    /* If autocommit is off, the function has already committed (or rolled back on failure). */
    printf("Date log written to the ODBC table.\n");
    exitCode = 0;

cleanup:
    if (pParams != NULL) {
        hr = FreeOdbcConnectParams(pParams);
        if (FAILED(hr)) {
            printf("FreeOdbcConnectParams failed: 0x%08lX\n", (unsigned long)hr);
        }
    }
    FreeDateItem(hEnd);
    FreeDateItem(hStart);
    if (hDateLog != NULL) {
        hr = CloseDateLogHandle(hDateLog);
        if (FAILED(hr)) {
            printf("CloseDateLogHandle [date log] failed: 0x%08lX\n", (unsigned long)hr);
        }
    }
    if (hHolidays != NULL) {
        hr = CloseDateLogHandle(hHolidays);
        if (FAILED(hr)) {
            printf("CloseDateLogHandle [holidays] failed: 0x%08lX\n", (unsigned long)hr);
        }
    }
    return exitCode;
}