Push the date log core (and optionally holidays) into an ODBC backend table
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).
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.
OdbcFindPlanInConnInternals) before building a new onepBulk->pHolidaysBulk)
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
);
[In] DATE_LOG_HANDLE
The date log whose core is written to the database.
ERROR_INVALID_PARAMETER).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().CloseDateLogHandle().[In] DATE_LOG_ODBC_LEVEL
Which of the handle's connection slots to use.
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.ERROR_INVALID_PARAMETER.[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.
ERROR_INVALID_PARAMETER).IsValidOdbcBulk(); otherwise ERROR_INVALID_PARAMETER is returned. If pHolidaysBulk is present, the handle must have a holidays core._Inout_); it must stay valid for the whole call. See SQL_DATE_BULK for the structure and its documented members.[In] DWORD
Reserved for future use.
0.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 value | Meaning |
|---|---|
S_OK | Data 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 HRESULT | Propagated from BuildOdbcDateColumnPlan, CreateOdbcDateLogTable, BulkInsertDateLogCoreToOdbc, IsOdbcTableExisting, IsOdbcTableColumnLayoutMatchingPlan, ReconcileOdbcDateColumnOrderFromTable, or ReconcileOdbcDateLogTableFetchPlan. |
SQLColumns() before computing which row ranges (before/after segments) still need inserting.InitializeDateLogHandle() and LoadDateLogHandle() to build the core; InitializeDateLogHandleOdbc() to reserve slot OdbcLevel (slots are reserved sequentially starting at DATE_LOG_ODBC_1); InitializeOdbcConnectParams() + AddParam to build a connection string; ConnectDateLogHandleToOdbcDatabase() to connect; then this function.ConnectDateLogHandleToOdbcDatabase() never returns it). The ODBC_CONNECT_PARAMS object is not retained; free it with FreeOdbcConnectParams() once connected.EndDateLogOdbcTransaction() only for transactions you manage yourself; use SetDateLogOdbcConnectAttr() to change connection attributes.DuplicateDateLogHandle()) and do not close the handle while this call is running. The shared context, including all ODBC connections, is released only when the last handle referencing it is closed with CloseDateLogHandle() (reference counted).pBulk->pHolidaysBulk is supplied; it is independent of the main table.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).
#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;
}