Set an ODBC connection attribute for a DATE_LOG_HANDLE's connection
SetDateLogOdbcConnectAttr sets or modifies a connection attribute (via SQLSetConnectAttr) for the ODBC connection identified by OdbcLevel on the given DATE_LOG_HANDLE. It resolves the connection context and delegates to OdbcSetConnectAttrImpl() in odbc.c.
It is commonly used to disable autocommit (SQL_ATTR_AUTOCOMMIT = SQL_AUTOCOMMIT_OFF) before grouping several statements into one transaction that is then ended with EndDateLogOdbcTransaction. The attribute is applied to the connection that is owned by the date log handle; the connection must already have been established.
OdbcLevel (up to 4 connections per handle).SQL_ATTR_TRACEFILE and SQL_ATTR_CONNECTION_POOLING are not supported.ValuePtr and StringLength follows the ODBC rules for SQLSetConnectAttr.GetDateLogOdbcConnectAttr to read an attribute back.
HRESULT _Must_inspect_result_ _Success_(SUCCEEDED(return)) SetDateLogOdbcConnectAttr(
_In_ DATE_LOG_HANDLE hDateLogHandle,
_In_ DATE_LOG_ODBC_LEVEL OdbcLevel,
_In_ SQLINTEGER Attribute,
_In_reads_bytes_opt_(StringLength) SQLPOINTER ValuePtr,
_In_ SQLINTEGER StringLength
);
The function takes exactly five arguments, in this order. SQLINTEGER and SQLPOINTER come from <sqltypes.h> (included by datelog.h); the SQL_ATTR_* constants come from the ODBC headers <sql.h>/<sqlext.h>.
[In] DATE_LOG_HANDLE
A valid DATE_LOG_HANDLE, DATE_RANGE_HANDLE or HOLIDAYS_HANDLE that owns the connection.
HRESULT.CloseDateLogHandle.[In] DATE_LOG_ODBC_LEVEL
Which of up to 4 connections to target (DATE_LOG_ODBC_1 through DATE_LOG_ODBC_4).
InitializeDateLogHandleOdbc and connected with ConnectDateLogHandleToOdbcDatabase. If no connection exists at that level, HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE_STATE) is returned.[In] SQLINTEGER
The SQL_ATTR_* connection attribute identifier to set.
SQL_ATTR_TRACEFILE and SQL_ATTR_CONNECTION_POOLING, which are unsupported (HRESULT_FROM_WIN32(ERROR_CALL_NOT_IMPLEMENTED)).[In, Opt] SQLPOINTER
The new attribute value, or a pointer to it, according to the ODBC rules for the attribute.
StringLength bytes; for integer attributes the value itself is passed in the pointer (for example (SQLPOINTER)(ULONG_PTR)SQL_AUTOCOMMIT_OFF)._In_reads_bytes_opt_) only where the attribute semantics permit it.[In] SQLINTEGER
The length in bytes of ValuePtr if it is a string; otherwise SQL_IS_INTEGER, SQL_IS_UINTEGER or SQL_IS_POINTER.
| Value | Meaning |
|---|---|
S_OK | The attribute was set successfully. |
HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE_STATE) | No connection has been established yet at the requested OdbcLevel. |
HRESULT_FROM_WIN32(ERROR_CALL_NOT_IMPLEMENTED) | The attribute is unsupported (SQL_ATTR_TRACEFILE or SQL_ATTR_CONNECTION_POOLING). |
Other failure HRESULT | Invalid parameters or an ODBC driver failure. |
Test with FAILED/SUCCEEDED; the result must be inspected. The library’s own ERR_* codes are listed in date_error.h; this function’s documented results are the Win32-derived values above.
This page does not document a locking guarantee for the attribute change. Do not change an attribute on a connection level from one thread while another thread is using the same level (for example executing a transaction through ReplaceDateLogHandleIdentifiers). Serialize access per handle and level, or use a different OdbcLevel for each thread.
CloseDateLogHandle; attribute changes apply to the live connection and are lost with it.FreeOdbcConnectParams.InitializeDateLogHandleOdbc → ConnectDateLogHandleToOdbcDatabase → SetDateLogOdbcConnectAttr → your statements → EndDateLogOdbcTransaction.GetDateLogOdbcConnectAttr reads an attribute back (same handle, level and attribute).ReplaceDateLogHandleIdentifiers manages autocommit and isolation itself for the duration of its call; see its page.EndDateLogOdbcTransaction with bCommit = TRUE. Always end the transaction, with commit or rollback, before closing the handle.GetDateLogOdbcConnectAttrEndDateLogOdbcTransactionInitializeDateLogHandleOdbcConnectDateLogHandleToOdbcDatabaseInitializeOdbcConnectParamsFreeOdbcConnectParamsReplaceDateLogHandleIdentifiersFetchDateLogHandleToOdbcDatabaseCloseDateLogHandleThe sample builds a date log, reserves and connects ODBC slot DATE_LOG_ODBC_1, turns autocommit off with SetDateLogOdbcConnectAttr, reads the value back with GetDateLogOdbcConnectAttr, commits with EndDateLogOdbcTransaction, and restores autocommit. An open transaction is rolled back on error paths, and the connection parameters, date log and holidays handle are released on every path.
#include <Windows.h>
#include <sql.h>
#include <sqlext.h>
#include <stdio.h>
#include "datelog.h"
static BOOL MakeFileTime(WORD wYear, WORD wMonth, WORD wDay, PFILETIME pFt)
{
SYSTEMTIME st = { 0 };
st.wYear = wYear;
st.wMonth = wMonth;
st.wDay = wDay;
return SystemTimeToFileTime(&st, pFt);
}
/* Creates a holidays handle and loads it with the range [first, last]. */
static HRESULT BuildHolidays(PFILETIME pFirst, PFILETIME pLast, HOLIDAYS_HANDLE *phHolidays)
{
HRESULT hr = S_OK;
HOLIDAYS_HANDLE hHolidays = NULL;
DATE_ITEM_HANDLE hFirst = NULL;
DATE_ITEM_HANDLE hLast = NULL;
*phHolidays = NULL;
hr = InitializeDateLogHandle(NULL, DATE_LOG_MAX, NULL, &hHolidays, HandleIsForHolidays);
if (FAILED(hr)) goto cleanup;
hr = InitializeDateItem(hHolidays, pFirst, NULL, &hFirst);
if (FAILED(hr)) goto cleanup;
hr = InitializeDateItem(hHolidays, pLast, NULL, &hLast);
if (FAILED(hr)) goto cleanup;
hr = LoadDateLogHandle(NULL, hFirst, hLast, hHolidays, NULL, 0);
if (FAILED(hr)) goto cleanup;
*phHolidays = hHolidays;
hHolidays = NULL;
cleanup:
FreeDateItem(hLast);
FreeDateItem(hFirst);
if (hHolidays != NULL) {
(void)CloseDateLogHandle(hHolidays);
}
return hr;
}
/* Creates a date log or range handle of the given type and generates [first, last] into it. */
static HRESULT BuildCollection(HOLIDAYS_HANDLE hHolidays, DateHandleType type,
PFILETIME pFirst, PFILETIME pLast, DATE_LOG_HANDLE *phCollection)
{
HRESULT hr = S_OK;
DATE_LOG_HANDLE hCollection = NULL;
DATE_ITEM_HANDLE hFirst = NULL;
DATE_ITEM_HANDLE hLast = NULL;
*phCollection = NULL;
hr = InitializeDateLogHandle(NULL, DATE_LOG_MAX, hHolidays, &hCollection, type);
if (FAILED(hr)) goto cleanup;
hr = InitializeDateItem(hHolidays, pFirst, NULL, &hFirst);
if (FAILED(hr)) goto cleanup;
hr = InitializeDateItem(hHolidays, pLast, NULL, &hLast);
if (FAILED(hr)) goto cleanup;
hr = LoadDateLogHandle(NULL, hFirst, hLast, hCollection, NULL, 0);
if (FAILED(hr)) goto cleanup;
*phCollection = hCollection;
hCollection = NULL;
cleanup:
FreeDateItem(hLast);
FreeDateItem(hFirst);
if (hCollection != NULL) {
(void)CloseDateLogHandle(hCollection);
}
return hr;
}
/* Reserves connection slot 1 on the log and connects it using the parameters. */
static HRESULT ConnectLog(DATE_LOG_HANDLE hDateLog, PODBC_CONNECT_PARAMS pParams)
{
HRESULT hr = InitializeDateLogHandleOdbc(hDateLog, DATE_LOG_ODBC_1);
if (FAILED(hr)) {
return hr;
}
return ConnectDateLogHandleToOdbcDatabase(hDateLog, pParams, DATE_LOG_ODBC_1);
}
int main(void)
{
int exitCode = 1;
HRESULT hr = S_OK;
FILETIME ftFirst = { 0 };
FILETIME ftLast = { 0 };
HOLIDAYS_HANDLE hHolidays = NULL;
DATE_LOG_HANDLE hDateLog = NULL;
PODBC_CONNECT_PARAMS pParams = NULL;
SQLUINTEGER ulAutoCommit = 0;
BOOL bTransactionOpen = FALSE;
if (!MakeFileTime(2025, 1, 2, &ftFirst) || !MakeFileTime(2025, 3, 31, &ftLast)) {
printf("Invalid calendar date.\n");
return 1;
}
hr = BuildHolidays(&ftFirst, &ftLast, &hHolidays);
if (FAILED(hr)) {
printf("BuildHolidays failed: 0x%08lX\n", (unsigned long)hr);
goto cleanup;
}
hr = BuildCollection(hHolidays, HandleIsForDatelog, &ftFirst, &ftLast, &hDateLog);
if (FAILED(hr)) {
printf("BuildCollection 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;
}
hr = pParams->AddParam(pParams, "Driver", "{ODBC Driver 17 for SQL Server}");
if (SUCCEEDED(hr)) hr = pParams->AddParam(pParams, "Server", "localhost\\SQLEXPRESS");
if (SUCCEEDED(hr)) hr = pParams->AddParam(pParams, "Database", "DateDatabase");
if (SUCCEEDED(hr)) hr = pParams->AddParam(pParams, "Trusted_Connection", "yes");
if (FAILED(hr)) {
printf("AddParam failed: 0x%08lX\n", (unsigned long)hr);
goto cleanup;
}
/* An established connection is required before an attribute can be set. */
hr = ConnectLog(hDateLog, pParams);
if (FAILED(hr)) {
printf("Connecting to the database failed: 0x%08lX\n", (unsigned long)hr);
goto cleanup;
}
(void)FreeOdbcConnectParams(pParams);
pParams = NULL;
/* Integer attribute: the value is passed in the pointer and StringLength is SQL_IS_UINTEGER. */
hr = SetDateLogOdbcConnectAttr(hDateLog, DATE_LOG_ODBC_1, SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)(ULONG_PTR)SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
if (FAILED(hr)) {
if (hr == HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE_STATE)) {
printf("No connection exists at DATE_LOG_ODBC_1.\n");
} else if (hr == HRESULT_FROM_WIN32(ERROR_CALL_NOT_IMPLEMENTED)) {
printf("That attribute is not supported.\n");
} else {
printf("SetDateLogOdbcConnectAttr failed: 0x%08lX\n", (unsigned long)hr);
}
goto cleanup;
}
bTransactionOpen = TRUE;
/* Read the value back to confirm it. */
hr = GetDateLogOdbcConnectAttr(hDateLog, DATE_LOG_ODBC_1, SQL_ATTR_AUTOCOMMIT,
&ulAutoCommit, (SQLINTEGER)sizeof(ulAutoCommit), NULL);
if (SUCCEEDED(hr)) {
printf("Autocommit is %s.\n", ulAutoCommit == SQL_AUTOCOMMIT_OFF ? "off" : "on");
}
/* ... execute the statements that make up the transaction here ... */
hr = EndDateLogOdbcTransaction(hDateLog, DATE_LOG_ODBC_1, TRUE);
if (FAILED(hr)) {
printf("Commit failed: 0x%08lX\n", (unsigned long)hr);
goto cleanup;
}
bTransactionOpen = FALSE;
/* Restore autocommit for later work on this connection. */
hr = SetDateLogOdbcConnectAttr(hDateLog, DATE_LOG_ODBC_1, SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)(ULONG_PTR)SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER);
if (FAILED(hr)) {
printf("Restoring autocommit failed: 0x%08lX\n", (unsigned long)hr);
goto cleanup;
}
exitCode = 0;
cleanup:
if (bTransactionOpen) {
/* Do not leave an uncommitted transaction behind on an error path. */
(void)EndDateLogOdbcTransaction(hDateLog, DATE_LOG_ODBC_1, FALSE);
}
if (pParams != NULL) {
(void)FreeOdbcConnectParams(pParams);
}
if (hDateLog != NULL) {
(void)CloseDateLogHandle(hDateLog);
}
if (hHolidays != NULL) {
(void)CloseDateLogHandle(hHolidays);
}
return exitCode;
}