Example: Use the CLI XA transaction connection attributes

This example shows how to use the CLI XA transaction connection attributes.

Note: By using the code examples, you agree to the terms of the Code license and disclaimer information.
/*************************************************************************
** file = CLIXAEXMP1.c
**
** Example of a typical flow of work in an XA transaction using the CLI.
** 
** XA Functions used:
** 
**		      xa_open()	   -- Open an XA resource for use in a transaction
**		      xa_prepare() -- Prepare for commitment of work in the transaction
**		      xa_commit()  -- Commit work done in the transaction
**
** CLI Functions used:
**
**        SQLAllocHanle    SQLBindParameter    SQLDisconnect
**        SQLError         SQLExecute          SQLFreeHandle
**        SQLPrepare       SQLSetConnectAttr   SQLSetEnvAttr 
**
** This example will:
**	 - Open the XA transaction manager
**	 - Open a CLI connection and start a transaction for it using SQL_TXN_CREATE
**  - Do some commitable CLI work under this transaction
**  - End the transaction on the first connection using SQL_TXN_END
**  - Close the first CLI connection and open a second connection
**  - Use the SQL_TXN_FIND option to find the previous transaction
**  - Do more commitable work on this transaction and end the transaction
**  - Use the XA APIs to prepare and commit the work
************************************************************************************/
#define _XA_PROTOTYPES
#define _MULTI_THREADED
#include <xa.h>
#include <stdio.h>
#include <string.h>
#include <sqlcli.h>
#include <time.h>
#include <stdlib.h>

void genXid(XID *xid) {
    time_t     t;
    memset(xid, 0, sizeof(xid));
    xid->formatID = 69;
    xid->gtrid_length = 4;
    xid->bqual_length = 4;
    /* xid->data must be a globally unique naming identifier 
       when taking gtrid and bqual together - the example below
       is most likely not unique */
    /* gtrid contents */
    xid->data[0] = 0xFA;
    xid->data[1] = 0xED;
    xid->data[2] = 0xFA;
    xid->data[3] = 0xED;
    time(&t);
    /* bqual contents */
    xid->data[4] = (((int)t) >> 24) & 0xFF;
    xid->data[5] = (((int)t) >> 16) & 0xFF;
    xid->data[6] = (((int)t) >>  8) & 0xFF;
    xid->data[7] = (((int)t) >>  0) & 0xFF;
}

int main(int argc, char **argv)
{
/***************************************************/
/* Declarations Section                            */
/***************************************************/
    SQLHENV  henv;
    SQLHDBC  hdbc;
    SQLHSTMT hstmt;
    SQLRETURN rtnc;
    SQLINTEGER attr;
    SQLINTEGER int_buffer;
    SQLINTEGER rlength;
    SQLINTEGER buffint;
    SQLINTEGER ilen;
    SQLCHAR s[80]; 
    SQLCHAR state[10];
    SQLCHAR buffer[600];
    SQLCHAR sqlstr[600];
    SQLINTEGER natErr;
    SQLSMALLINT len;
    
    /* Declare local XA variables */
    struct TXN_STRUCT new;
    XID         xid;
    char        xaOpenFormat[128];
    int         mainRmid = 1;
    int         xaRc;

    /* Initialize the XA structure variable's (defined in sqlcli.h) */
    strcpy(new.tminfo,"MYPRODUCT");
    strcpy(new.reserved1,"");
    new.timeoutval = 0;
    new.locktimeout = 0;
    strcpy(new.reserved2,"");
    genXid(&xid);
    new.XID = &xid;

    /* Use the XA APIs to start the transaction manager */
    /* The xa_info argument for xa_open MUST include the THDCTL=C keyword 
       and value when using using CLI with XA transactions */
    sprintf(xaOpenFormat, "RDBNAME=*LOCAL THDCTL=C");
    xaRc = xa_open(xaOpenFormat, mainRmid, TMNOFLAGS);
    printf("xa_open(%s, %d, TMNOFLAGS) = %d\n",
           xaOpenFormat, mainRmid, xaRc);

    /* Setup the CLI resources */
    attr=SQL_TRUE;
    rtnc=SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&henv);
    rtnc=SQLSetEnvAttr(henv,SQL_ATTR_SERVER_MODE,&attr,0); /* set server mode */
    rtnc=SQLAllocHandle(SQL_HANDLE_DBC,henv,&hdbc);
    
    /* Mark the connection as an external transaction and connect */
    rtnc=SQLSetConnectAttr(hdbc,SQL_ATTR_TXN_EXTERNAL,&attr,0);
    rtnc=SQLConnect(hdbc,NULL,0,NULL,0,NULL,0);

    /* Start the transaction */
    new.operation =  SQL_TXN_CREATE;
    rtnc=SQLSetConnectAttr(hdbc,SQL_ATTR_TXN_INFO,&new,0);

    /* Do some CLI work */
    rtnc=SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt);
    strcpy(sqlstr,"insert into tab values(?)");
    rtnc=SQLPrepare(hstmt,sqlstr,SQL_NTS);
    rtnc=
    SQLBindParameter(hstmt,1,1,SQL_INTEGER,SQL_INTEGER,10,2,&buffint,0,&ilen);
    buffint=10; /* set the integer value to insert */
    rtnc=SQLExecute(hstmt);
    if (rtnc!=SQL_SUCCESS)
    {
	 printf("SQLExecute failed with return code: %i \n", rtnc);
     rtnc=SQLError(0, 0,hstmt, state, &natErr, buffer, 600, &len);
     printf("%i  is the SQLCODE\n",natErr);
     printf("%i  is the length of error text\n",len);
     printf("%s  is the state\n",state );
     printf("%s  \n",buffer);
    }
    else
	  printf("SQLExecute succeeded, value %i inserted \n", buffint);
   
    /* End the transaction */
    new.operation =  SQL_TXN_END;  
    rtnc=SQLSetConnectAttr(hdbc,SQL_ATTR_TXN_INFO,&new,0);

    /* Cleanup and disconnect from the first connection */
    rtnc=SQLFreeHandle(SQL_HANDLE_STMT,hstmt);
 	rtnc=SQLDisconnect(hdbc);
    
    /* Mark the second connection as an external transaction and connect */
    attr=SQL_TRUE;
    rtnc=SQLSetConnectAttr(hdbc,SQL_ATTR_TXN_EXTERNAL,&attr,0);
    rtnc=SQLConnect(hdbc,NULL,0,NULL,0,NULL,0);
    
    /* Find the open transaction from the first connection */
    new.operation =  SQL_TXN_FIND;
    rtnc=SQLSetConnectAttr(hdbc,SQL_ATTR_TXN_INFO,&new,0);

    /* Do some CLI work on the second connection */
    rtnc=SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt);
    strcpy(sqlstr,"insert into tab values(?)");
    rtnc=SQLPrepare(hstmt,sqlstr,SQL_NTS);
    rtnc=
    SQLBindParameter(hstmt,1,1,SQL_INTEGER,SQL_INTEGER,10,2,&buffint,0,&ilen);
    buffint=15; /* set the integer value to insert */
    rtnc=SQLExecute(hstmt);
    if (rtnc!=SQL_SUCCESS)
    {
	 printf("SQLExecute failed with return code: %i \n", rtnc);
     rtnc=SQLError(0, 0,hstmt, state, &natErr, buffer, 600, &len);
     printf("%i  is the SQLCODE\n",natErr);
     printf("%i  is the length of error text\n",len);
     printf("%s  is the state\n",state );
     printf("%s  \n",buffer);
    }
    else
	  printf("Second SQLExecute succeeded, value %i inserted \n", buffint);
   
    /* End the transaction */
    new.operation =  SQL_TXN_END;  
    rtnc=SQLSetConnectAttr(hdbc,SQL_ATTR_TXN_INFO,&new,0);
    
    /* Now, use XA to prepare/commit transaction  */
    /* Prepare to commit */
    xaRc = xa_prepare(&xid, mainRmid, TMNOFLAGS);
    printf("xa_prepare(xid, %d, TMNOFLAGS) = %d\n",mainRmid, xaRc);

    /* Commit */
    if (xaRc != XA_RDONLY) {
        xaRc = xa_commit(&xid, mainRmid, TMNOFLAGS);
        printf("xa_commit(xid, %d, TMNOFLAGS) = %d\n", mainRmid, xaRc);
    }
    else {
        printf("xa_commit() skipped for read only TX\n");
    }

    /* Cleanup the CLI resources */
    rtnc=SQLFreeHandle(SQL_HANDLE_STMT,hstmt);
 	rtnc=SQLDisconnect(hdbc);
 	rtnc=SQLFreeHandle(SQL_HANDLE_DBC,hdbc);
 	rtnc=SQLFreeHandle(SQL_HANDLE_ENV,henv);
	return 0;
}