/**********************************************************************
* FILENAME :        Transactions.c
*
* DESCRIPTION :
*       Example illustrates use of SQLSetConnectAttr with SQL_ATTR_AUTOCOMMIT
*       and SQLEndTran to manually or automatically commit an update
*       on a table with an identifier field
*
* ODBC USAGE :
*       Prompts for Auto Commit or Manual Commit mode
*       SQLSetConnectAttr with SQL_ATTR_AUTOCOMMIT to set commit mode
*       SQLBindParameter to bind 4 parameters into
*       SQLPrepare to get ready an INSERT
*       SQLBindParameter to establish parameter bindings for the INSERT
*       Prompts for data to be used in the insert
*       SQLExecute to execute the statement
*       If Manual commit specified, asks whether SQLEndTran called or not to
*       either commit or rollback
*       Reads table and displays records.
*       NOTE: Behaviour of the identity column PersonID. Whether Commit or
*             Rollback is sent, this is incremented for each record. i.e.
*             a gap in the identity sequence will appear for records that where
*             used Rollback.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>

#include "util.c"

#define LASTNAME_LEN  255
#define FIRSTNAME_LEN 255
#define ADDRESS_LEN 255
#define CITY_LEN  255
#define TRUE 1
#define FALSE 0

SQLLEN iPersonID;
SQLCHAR strFirstName[FIRSTNAME_LEN];
SQLCHAR strLastName[LASTNAME_LEN];
SQLCHAR strAddress[ADDRESS_LEN];
SQLCHAR strCity[CITY_LEN];
SQLLEN  lenPersonID=0, lenFirstName=0, lenLastName=0, lenAddress=0, lenCity=0;

int main () {

    SQLHENV  henv  = SQL_NULL_HENV;   // Environment
    SQLHDBC  hdbc  = SQL_NULL_HDBC;   // Connection handle
    SQLHSTMT hstmt = SQL_NULL_HSTMT;  // Statement handle
    SQLRETURN retcode;

    char inputBuff[5];
    char commitMode='A';      // A - Auto,   M - Manual
    char completionType='C';  // C - COMMIT, R - ROLLBACK
    char reply=' ';

    // Get commit type - A Automatic, M Manual
    reply=getStr ("Commit Type (A - Auto, M - Manual)",
                                    inputBuff, sizeof(inputBuff), 'N');
    commitMode=inputBuff[0];
    commitMode=toupper(commitMode);
    if (commitMode != 'A' && commitMode != 'M') commitMode='A';

    // Allocate environment
    retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
                henv, SQL_HANDLE_ENV);

    // Set ODBC version
    retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
                                                (SQLPOINTER*)SQL_OV_ODBC3, 0);
    CHECK_ERROR(retcode, "SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION)",
                henv, SQL_HANDLE_ENV);

    // Allocate connection
    retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_DBC)",
                hdbc, SQL_HANDLE_DBC);

    // Set Login Timeout and ask for manual/auto commit
    retcode = SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
    CHECK_ERROR(retcode, "SQLSetConnectAttr(SQL_LOGIN_TIMEOUT)",
                hdbc, SQL_HANDLE_DBC);

    if (commitMode=='A') {
        printf ("Setting AUTOCOMMIT AUTO\n");
        retcode = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT,
                                                        (SQLPOINTER)TRUE, 0);
    }
    else {
        printf ("Setting AUTOCOMMIT MANUAL\n");
        retcode = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT,
        (SQLPOINTER)FALSE, 0);
    }

    CHECK_ERROR(retcode, "SQLSetConnectAttr(SQL_ATTR_AUTOCOMMIT)",
                                                        hdbc, SQL_HANDLE_DBC);

    // Connect to DSN
    retcode = SQLConnect(hdbc, (SQLCHAR*) "DATASOURCE", SQL_NTS,
                               (SQLCHAR*) NULL, 0, NULL, 0);
    CHECK_ERROR(retcode, "SQLConnect(DATASOURCE)", hdbc, SQL_HANDLE_DBC);

    // Allocate Statement Handle
    retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_STMT)",
                hstmt, SQL_HANDLE_STMT);

    // Bind Parameters to all fields
    retcode = SQLBindParameter(hstmt, 1,
                               SQL_PARAM_INPUT, SQL_C_CHAR,
                               SQL_CHAR, FIRSTNAME_LEN, 0,
                               strFirstName, FIRSTNAME_LEN, &lenFirstName);
    CHECK_ERROR(retcode, "SQLBindParameter(1)", hstmt, SQL_HANDLE_STMT);
    retcode = SQLBindParameter(hstmt, 2,
                               SQL_PARAM_INPUT, SQL_C_CHAR,
                               SQL_CHAR, LASTNAME_LEN, 0,
                               strLastName, LASTNAME_LEN, &lenLastName);
    CHECK_ERROR(retcode, "SQLBindParameter(2)", hstmt, SQL_HANDLE_STMT);
    retcode = SQLBindParameter(hstmt, 3,
                               SQL_PARAM_INPUT, SQL_C_CHAR,
                               SQL_CHAR, ADDRESS_LEN, 0,
                               strAddress, ADDRESS_LEN, &lenAddress);
    CHECK_ERROR(retcode, "SQLBindParameter(3)", hstmt, SQL_HANDLE_STMT);
    retcode = SQLBindParameter(hstmt, 4,
                               SQL_PARAM_INPUT, SQL_C_CHAR,
                               SQL_CHAR, CITY_LEN, 0,
                               strCity, CITY_LEN, &lenCity);
    CHECK_ERROR(retcode, "SQLBindParameter(4)", hstmt, SQL_HANDLE_STMT);

    // Prepare INSERT
    retcode = SQLPrepare(hstmt, (SQLCHAR*)
        "INSERT INTO TestTBL1 (FirstName, LastName, Address, City)"
                                                "VALUES (?, ?, ?, ?)", SQL_NTS);
    CHECK_ERROR(retcode, "SQLPrepare(INSERT)", hstmt, SQL_HANDLE_STMT);

    // Set up data and lengths
    memset(strFirstName, ' ', FIRSTNAME_LEN);
    memset(strLastName, ' ', LASTNAME_LEN);
    memset(strAddress, ' ', ADDRESS_LEN);
    memset(strCity, ' ', CITY_LEN);

    reply=getStr ("First Name", strFirstName, sizeof (strFirstName), 'N');
    reply=getStr ("Last  Name", strLastName, sizeof (strLastName), 'N');
    reply=getStr ("Address   ", strAddress, sizeof (strAddress), 'N');
    reply=getStr ("City      ", strCity, sizeof (strCity), 'N');

    // Remove string teminator
    strFirstName[strlen(strFirstName)]=' ';
    strLastName[strlen(strLastName)]=' ';
    strAddress[strlen(strAddress)]=' ';
    strCity[strlen(strCity)]=' ';

    // Set lengths to field lengths
    lenFirstName=sizeof(strFirstName);
    lenLastName=sizeof(strLastName);
    lenAddress=sizeof(strAddress);
    lenCity=sizeof(strCity);

    retcode = SQLExecute(hstmt);
    CHECK_ERROR(retcode, "SQLExecute(INSERT)", hstmt, SQL_HANDLE_STMT);

    if (commitMode=='M') {
        reply=getStr ("Completion Type (C - Commit, R - Rollback)",
                                            inputBuff, sizeof(inputBuff), 'N');
        completionType=inputBuff[0];
        completionType=toupper(completionType);
        if (completionType != 'C' && completionType != 'R') commitMode='R';
        if (completionType=='C') {
            retcode = SQLEndTran (SQL_HANDLE_DBC, hdbc, SQL_COMMIT);
            CHECK_ERROR(retcode, "SQLEndTran(SQL_COMMIT)",
                        hdbc, SQL_HANDLE_DBC);
            printf ("Transaction Committed (status %i)\n", retcode);
        } else {
            retcode = SQLEndTran (SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK);
            CHECK_ERROR(retcode, "SQL_HANDLE_DBC(SQL_ROLLBACK)",
                        hdbc, SQL_HANDLE_DBC);
            printf ("Transaction Rolled Back (status %i)\n", retcode);
        }
    } else {
        printf ("Transaction Auto Committed\n");
    }

    // Read table back ...
    retcode = SQLFreeStmt(hstmt, SQL_CLOSE);
    CHECK_ERROR(retcode, "SQLFreeStmt()", hstmt, SQL_HANDLE_STMT);

    retcode = SQLExecDirect(hstmt, (SQLCHAR *)
                                        "SELECT * FROM TestTBL1", SQL_NTS);
    CHECK_ERROR(retcode, "SQLExecDirect(SELECT)", hstmt, SQL_HANDLE_STMT);
    for ( retcode = SQLFetch(hstmt) ;
          retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO ;
          retcode = SQLFetch(hstmt) ) {
        memset(strFirstName, ' ', FIRSTNAME_LEN);
        retcode = SQLGetData(hstmt, 1, SQL_C_USHORT,
                            (SQLPOINTER)&iPersonID, 0, NULL);
        retcode = SQLGetData(hstmt, 2, SQL_C_CHAR,
                             strFirstName, FIRSTNAME_LEN, &lenFirstName);
        retcode = SQLGetData(hstmt, 3, SQL_C_CHAR,
                             strLastName,  LASTNAME_LEN,  &lenLastName);
        retcode = SQLGetData(hstmt, 4, SQL_C_CHAR,
                            strAddress,   ADDRESS_LEN,   &lenAddress);
        retcode = SQLGetData(hstmt, 5, SQL_C_CHAR,
                             strCity, CITY_LEN, &lenCity);

        printf ("\nPerson ID  : %i", (int)iPersonID);
        printf ("\nFirst Name : %.10s", strFirstName);
        printf ("\nLast Name  : %.10s", strLastName);
        printf ("\nAddress    : %.10s", strAddress);
        printf ("\nCity       : %.10s\n", strCity);
    }

exit:

    printf ("\nComplete.\n");

    // Free handles
    // Statement
    if (hstmt != SQL_NULL_HSTMT)
        SQLFreeHandle(SQL_HANDLE_STMT, hstmt);

    // Connection
    if (hdbc != SQL_NULL_HDBC) {
        SQLDisconnect(hdbc);
        SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
    }

    // Environment
    if (henv != SQL_NULL_HENV)
        SQLFreeHandle(SQL_HANDLE_ENV, henv);

    return 0;
}

Further information