MultipleRM Program Examples

This chapter describes examples of MultipleRM programs, environment configuration, and makefiles for compilation in TCS.

1. Using Oracle and Tibero

In TCS, Oracle and Tibero can be used through the server library. This section describes examples of MultipleRM programs, environment configuration, and makefile for compilation.

1.1. Program Examples

The following shows sample TCS programs that use both Oracle and Tibero. The program examples can be used by writing a server program with Embedded SQL and pre-compiling it with the pre-compiler provided by each database vendor.

Service Execution

The following is program source code that executes a service.

#include <stdio.h>
#include <usrinc/atmi.h>

extern int oracle_call(TPSVCINFO *msg);
extern int tibero_call(TPSVCINFO *msg);

MDB_SVC1(TPSVCINFO *msg)
{
    int ret;
    int fail = 0;

    ret = oracle_call(msg);
    if (ret < 0) {
        fail = 1;
    }

    ret = tibero_call(msg);
    if (ret < 0) {
        fail = 1;
    }

    if ( fail ) {
        tpreturn(TPFAIL,0,(char *)msg->data, msg->len,0);
    }

    tpreturn(TPSUCCESS,0,(char *)msg->data, msg->len,0);
}
Oracle Call Function

The following program uses a function to call Oracle.

#include <stdio.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>

EXEC SQL include sqlca.h;

EXEC SQL begin declare section;
    char h_addr[100];
EXEC SQL end declare section;

int oracle_call(TPSVCINFO *msg)
{
    int     i;

    strcpy(h_addr, msg->data);

    EXEC SQL INSERT
        INTO account( account_id, address, phone )
        VALUES ( tmax_seq.nextval,:h_addr, '007-1234' );

    if ( sqlca.sqlcode != 0 ){
        printf( "insert failed sqlcode = %d\n",sqlca.sqlcode );
        tpreturn( TPFAIL, -1, NULL, 0, 0 );
    }

    printf("ORACLE insert Success\n");

    return 0;
}
Tibero Call Function

The following program uses a function to call Tibero.

#include <stdio.h>
#include <string.h>
#include <usrinc/atmi.h>

EXEC SQL include sqlca.h;

EXEC SQL begin declare section;
    char h_addr[100];
EXEC SQL end declare section;

int tibero_call(TPSVCINFO *msg)
{
    strcpy(h_addr, msg->data);

    EXEC SQL INSERT
        INTO account( account_id, address, phone )
        VALUES ( tmax_seq.nextval,:h_addr, '007-1234' );

    if ( sqlca.sqlcode != 0 ){
        printf( "tibero insert failed sqlcode = %d\n",sqlca.sqlcode );
        tpreturn( TPFAIL, -1, NULL, 0, 0 );
    }

    printf("TIBERO insert Success\n");

    return 0;
}

1.2. Environment Configuration

The following shows the environment configuration of MultipleRM that uses both Oracle and Tibero.

*DOMAIN
domain        SHMKEY   = 81522, MAXUSER = 10000,
              MINCLH   = 1, MAXCLH = 1,
              TPORTNO  = 11500,  BLOCKTIME = 30,
              MAXSACALL = 1024, MAXCACALL = 1024,
              MAXMTMAX = 3, MAXSTMAX = 10

*NODE
node1         TMAXDIR  = "/home/tmax",
              APPDIR   = "/home/tmax/appbin",
              PATHDIR  = "/home/tmax/path",
              TLOGDIR  = "/home/tmax/log/tlog",
              ULOGDIR  = "/home/tmax/log/ulog",
              SLOGDIR  = "/home/tmax/log/slog"

*SVRGROUP
svg_s1        NODENAME = node1,
              DBNAME = ORACLE,
              OPENINFO ="Oracle_Xa+SqlNet=TMAX+Acc=P/scott/tiger+SesTm=60",
              TMSNAME = tms_ora,
              SVGTYPE = STMAX

svg_s2        NODENAME = node1,
              DBNAME = TIBERO,
              OPENINFO ="TIBERO_XA:user=scott,pwd=tiger, sestm=60,db=TMAX",
              TMSNAME = tms_tbr,
              SVGTYPE = STMAX

1.3. Compilation

This section describes an example of compiling MultipleRM using Oracle and Tibero.

When building a program, if a library (e.g. liboras.so) that defines the _tmax_xasw_init() function is linked, follow the process of Extracting a library list in RM File. Otherwise, follow the process of Using definitions in the configuration file. -lnodb must be included in the LIBS (Tmax library) item of the makefile.

Makefile contents may vary depending on the operating system.

The following is a makefile that compiles a MultipleRM server program on 32-bit Linux.

# Oracle Env
ORALIBDIR = /home/OraHome1/lib/
ORALIB = -lclntsh cat /home/OraHome1/lib/ldflags   cat /home/OraHome1/lib/sysliblist -ldl -lm

# Tibero Env
TBRLIB = -ltbxa -ltbertl
TBRLIBDIR = $(TB_HOME)/client/lib
TBRINCDIR = $(TB_HOME)/client/include

$ Server build
TARGET  = $(COMP_TARGET)
APOBJS  = $(TARGET).o
AP_ORA_OBJS = $(TARGET)_ora.o
AP_TBR_OBJS = $(TARGET)_tbr.o
NSDLOBJ = $(TMAXDIR)/lib/sdl.o

LIBS    = -lnsl -lsvr -lnodb

OBJS    = $(AP_ORA_OBJS) $(AP_TBR_OBJS) $(SVCTOBJ) $(APOBJS)
SVCTOBJ = $(TARGET)_svctab.o

CFLAGS  =  -O -I$(TMAXDIR)

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
TMAXLIBDIR  = $(TMAXDIR)/lib

#
.SUFFIXES : .c

.c.o:
    echo $(OBJS)
    $(CC) $(CFLAGS) -c $<

#
# server compile
#
all: $(TARGET)


$(TARGET):$(OBJS)
    echo $(OBJS)
    $(CC) $(CFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) -L$(TBRLIBDIR) -L$(ORALIBDIR) $(ORALIB) $(TBRLIB) $(OBJS) $(LIBS) $(NSDLOBJ)
    mv $(TARGET) $(APPDIR)/.
    rm -f $(OBJS)

$(AP_ORA_OBJS):
    echo $(OBJS)
    proc iname=$(TARGET)_ora.pc include=$(TMAXDIR)
    $(CC) $(CFLAGS) -c $(TARGET)_ora.c

$(AP_TBR_OBJS):
    echo $(OBJS)
    tbpc iname=$(TARGET)_tbr.tbc include=$(TMAXDIR)
    $(CC) $(CFLAGS) -I$(TBRINCDIR) -c $(TARGET)_tbr.c

$(APOBJS):
    echo $(OBJS)
    $(CC) $(CFLAGS) -c $(TARGET).c

$(SVCTOBJ):
    echo $(OBJS)
    cp -f $(SVCTDIR)/$(TARGET)_svctab.c .
    touch ./$(TARGET)_svctab.c
    $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c

#
clean:
    -rm -f *.o core $(TARGET) $(TARGET).lis

2. Using Two Oracle Instances

Two Oracle instances can be used in TCS through the server library. This section describes examples of programs that use two Oracle instances, environment configuration, and makefile for compilation.

2.1. Program Examples

The following example shows transaction processing on a single server that uses two Oracle RM instances.

Since the same Oracle client library is used, the user must specify the instance to use when executing a query from user code. For xa_open in Oracle, an OCI function, such as xaoSvcCtx or xaoEnv, can be used to obtain a connection to the desired instance.

Service Execution

The following is program source code that executes a service.

#include <stdio.h>
#include <usrinc/atmi.h>

extern int oracle_call(TPSVCINFO *msg);

MDB_SVC2(TPSVCINFO *msg)
{
    int ret;
    int fail = 0;

    ret = oracle_call(msg);
    if (ret < 0) {
        fail = 1;
    }

    if ( fail ) {
        tpreturn(TPFAIL,0,(char *)msg->data, msg->len,0);
    }

    tpreturn(TPSUCCESS,0,(char *)msg->data, msg->len,0);
}
Oracle Call Function

The following program uses a function to call Oracle.

#include <stdio.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>
#include <oci.h>

static void checkerr(errhp, status)
OCIError *errhp;
sword status;
{
    text errbuf[512];
    sb4 errcode;

    switch (status)
    {
        case OCI_SUCCESS:
            break;
        case OCI_SUCCESS_WITH_INFO:
            break;
        case OCI_NEED_DATA:
            break;
        case OCI_NO_DATA:
            break;
        case OCI_ERROR:  /* get the error back and display on the screen */
            (void) OCIErrorGet ((dvoid *) errhp, (ub4) 1, (text *) NULL, &errcode,
                                errbuf, (ub4) sizeof(errbuf), (ub4) OCI_HTYPE_ERROR);
            (void) printf("Error - %s\n", errbuf);
            break;
        case OCI_INVALID_HANDLE:
            break;
        case OCI_STILL_EXECUTING:
            break;
        case OCI_CONTINUE:
            break;
        default:
            break;
    }
}

int oracle_call(TPSVCINFO *msg)
{
    int     i;
    OCIEnv *envhp;
    OCIServer *srvhp;
    OCIError *errhp;
    OCISvcCtx *svchp;
    OCISession *usrhp;
    OCIStmt *stmthp;
    dvoid *tmp;
    char *sql = {"INSERT "
        "INTO hahehiho_account( account_id, address, phone ) "
        "VALUES ( skt_seq.nextval,'asdf', '007-1234' )"};

    char *sql2 = {"INSERT "
        "INTO hahehiho_account( account_id, address, phone ) "
        "VALUES ( skt_seq.nextval,'fdsa', '700-1234' )"};

    svchp = xaoSvcCtx("A");
    envhp = xaoEnv("A");
    printf("oracle call A octxt[%d]\n", svchp);

    OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &errhp, (ub4) OCI_HTYPE_ERROR,
                    52, (dvoid **) &tmp);

    OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &stmthp, (ub4) OCI_HTYPE_STMT,
                    50, (dvoid **) &tmp);

    checkerr(errhp, OCIStmtPrepare(stmthp, errhp,(text *)sql, (ub4)strlen(sql),
            (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT));

    checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
            (OCISnapshot *) NULL, (OCISnapshot *) NULL,
            (ub4) OCI_DEFAULT));

    svchp = xaoSvcCtx("B");
    envhp = xaoEnv("B");
    printf("oracle call B octxt[%d]\n", svchp);

    OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &errhp, (ub4) OCI_HTYPE_ERROR,
                           52, (dvoid **) &tmp);

    OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &stmthp,
               (ub4) OCI_HTYPE_STMT, 50, (dvoid **) &tmp);

    checkerr(errhp, OCIStmtPrepare(stmthp, errhp,(text *)sql2, (ub4)strlen(sql2),
            (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT));

    checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
            (OCISnapshot *) NULL, (OCISnapshot *) NULL,
            (ub4) OCI_DEFAULT));

    return 0;
}

2.2. Environment Configuration and Compilation

The following shows the environment configuration and compilation of MultipleRM that uses two Oracle instances.

Environment Configuration

The following is a MultipleRM server configuration example.

*DOMAIN
domain        SHMKEY   = 81522, MAXUSER = 10000,
              MINCLH   = 1, MAXCLH = 1,
              TPORTNO  = 11500,  BLOCKTIME = 30,
              MAXSACALL = 1024, MAXCACALL = 1024,
              MAXMTMAX = 3, MAXSTMAX = 10

*NODE
node1         TMAXDIR  = "/home/tmax",
              APPDIR   = "/home/tmax/appbin",
              PATHDIR  = "/home/tmax/path",
              TLOGDIR  = "/home/tmax/log/tlog",
              ULOGDIR  = "/home/tmax/log/ulog",
              SLOGDIR  = "/home/tmax/log/slog"

*SVRGROUP
svg_s1        NODENAME = node1,
              DBNAME = ORACLE,
              OPENINFO ="Oracle_Xa+SqlNet=TMAX1+Acc=P/scott/tiger+SesTm=60+DB=A",
              TMSNAME = tms_ora,
              SVGTYPE = STMAX,
              RMID = 0

svg_s2        NODENAME = node1,
              DBNAME = ORACLE,
              OPENINFO ="Oracle_Xa+SqlNet=TMAX2+Acc=P/scott/tiger+SesTm=60+DB=B",
              TMSNAME = tms_ora,
              SVGTYPE = STMAX,
              RMID = 1

svgm1         NODENAME = node1,
              SVGLIST = "svg_s2,svg_s1",
              SVGTYPE = MTMAX

*SERVER
sample SVGNAME = svgm1, LOGLVL = DEBUG4

*SERVICE
MDB_SVC2 SVRNAME = sample
Makefile

-lnodb must be included in the LIBS item of the makefile. The following is a makefile example that compiles a MultipleRM server program on 32-bit Linux.

# Oracle Env
ORALIBDIR = /home/OraHome1/lib/
ORALIB = -lclntsh cat /home/OraHome1/lib/ldflags   cat /home/OraHome1/lib/sysliblist -ldl -lm

LIBS    = -lnsl -lsvrmrm

OBJS    = $(AP_ORA_OBJS) $(AP_TBR_OBJS) $(SVCTOBJ) $(APOBJS)
SVCTOBJ = $(TARGET)_svctab.o

CFLAGS  =  -O -I$(TMAXDIR)

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svctTARGET  = $(COMP_TARGET)
APOBJS  = $(TARGET).o
AP_ORA_OBJS = $(TARGET)_ora.o
NSDLOBJ = $(TMAXDIR)/lib/sdl.o

LIBS    = -lnsl -lsvr -lnodb

OBJS    = $(AP_ORA_OBJS) $(SVCTOBJ) $(APOBJS)
SVCTOBJ = $(TARGET)_svctab.o

CFLAGS  =  -O -I$(TMAXDIR) -I$(ORACLE_HOME)/include -I$(ORACLE_HOME)/rdbms/demo -I$(ORACLE_HOME)/rdbms/public

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
TMAXLIBDIR  = $(TMAXDIR)/lib

#
.SUFFIXES : .c

.c.o:
    echo $(OBJS)
    $(CC) $(CFLAGS) -c $<

#
# server compile
#
all: $(TARGET)


$(TARGET):$(OBJS)
    $(CC) $(CFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) -L$(ORALIBDIR) $(ORALIB)  $(OBJS) $(LIBS) $(NSDLOBJ)
    mv $(TARGET) $(APPDIR)/.
    rm -f $(OBJS)

$(AP_ORA_OBJS):
    $(CC) $(CFLAGS) -c $(TARGET)_ora.c

$(APOBJS):
    $(CC) $(CFLAGS) -c $(TARGET).c

$(SVCTOBJ):
    echo $(OBJS)
    cp -f $(SVCTDIR)/$(TARGET)_svctab.c .
    touch ./$(TARGET)_svctab.c
    $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c

#
clean:
    -rm -f *.o core $(TARGET) $(TARGET).lis

Makefile contents may vary depending on the operating system.

3. Using Oracle and DB2

Oracle and DB2 can be used in TCS using the server library. This section provides examples of programs that use Oracle and DB2, as well as examples of makefiles for configuration and compilation.

3.1. Program Examples

The following is a TCS example program that uses Oracle and DB2 together. This program example can be written using embedded SQL and compiled using a precompiler provided by each database vendor.

Service Execution

The following is program source code that executes a service.

#include <stdio.h>
#include <ctype.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>

extern int SVC2301TX_1_oracle(TPSVCINFO *msg);
extern int SVC2301TX_1_db2(TPSVCINFO *msg);
extern int SVC2301TX_2_oracle(TPSVCINFO *msg);
extern int SVC2301TX_2_db2(TPSVCINFO *msg);
extern int SVC2301TX_3_oracle(TPSVCINFO *msg);
extern int SVC2301TX_3_db2(TPSVCINFO *msg);
extern int SVC2301TX_4_oracle(TPSVCINFO *msg);
extern int SVC2301TX_4_db2(TPSVCINFO *msg);

SVC2301TX_1( TPSVCINFO *msg )
{
    int ret;
    int fail = 0;
    char *rcvbuf;

    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

    ret = SVC2301TX_1_oracle(msg);
    if (ret < 0) {
        fail = 1;
    }

    ret = SVC2301TX_1_db2(msg);
    if (ret < 0) {
        fail = 1;
    }

    if ( fail ) {
        printf("SVC2301TX_1  FAIL\n");
        tpreturn(TPFAIL,-1,NULL, 0,0);
    }

    strcpy(rcvbuf, "Multiple RM Insert Success");

    tpreturn( TPSUCCESS, 0, rcvbuf, strlen(rcvbuf), 0 );
}

SVC2301TX_2( TPSVCINFO *msg )
{
    int ret;
    int fail = 0;
    char *rcvbuf;

    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    ret = SVC2301TX_2_oracle(msg);
    if (ret < 0) {
        fail = 1;
    }

    ret = SVC2301TX_2_db2(msg);
    if (ret < 0) {
        fail = 1;
    }

    if ( fail ) {
        printf("SVC2301TX_2  FAIL\n");
        tpreturn(TPFAIL,-1,NULL, 0,0);
    }

    strcpy(rcvbuf, "Multiple RM Update Success");
    tpreturn( TPSUCCESS, 0, rcvbuf, strlen(rcvbuf), 0 );
}

SVC2301TX_3( TPSVCINFO *msg )
{
    int ret;
    int fail = 0;
    char *rcvbuf;

    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    ret = SVC2301TX_3_oracle(msg);
    if (ret < 0) {
        fail = 1;
    }

    ret = SVC2301TX_3_db2(msg);
    if (ret < 0) {
        fail = 1;
    }

    if ( fail ) {
        printf("SVC2301TX_3  FAIL\n");
        tpreturn(TPFAIL,-1,NULL, 0,0);
    }

    strcpy(rcvbuf, "Multiple RM Delete Success");
    tpreturn( TPSUCCESS, 0, rcvbuf, strlen(rcvbuf), 0 );
}

SVC2301TX_4( TPSVCINFO *msg )
{
    int ret;
    int fail = 0;
    char *rcvbuf;

    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    ret = SVC2301TX_4_oracle(msg);
    if (ret < 0) {
        fail = 1;
    }

    ret = SVC2301TX_4_db2(msg);
    if (ret < 0) {
        fail = 1;
    }

    if ( fail ) {
        printf("SVC2301TX_4  FAIL\n");
        tpreturn(TPFAIL,-1,NULL, 0,0);
    }

    strcpy(rcvbuf, "Multiple RM Select Success");
    tpreturn( TPSUCCESS, 0, rcvbuf, strlen(rcvbuf), 0 );
}
Oracle Call Function

The following program uses a function to call Oracle.

#include <stdio.h>
#include <ctype.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>

EXEC SQL INCLUDE SQLCA.H;

EXEC SQL BEGIN DECLARE SECTION;
    int  h_empno;
    char h_ename[MAXLEN];
    char h_job[MAXLEN];
    char h_date[MAXLEN];
    float h_sal;
    int  h_count;
    char h_xid[100];
EXEC SQL END DECLARE SECTION;

SVC2301TX_1_oracle( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];
    TXINFO info;

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    h_empno = h_sal = 0;

    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );
    memset( h_xid , 0x00, sizeof( h_xid  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );

    if (tx_info(&info)==1) {
            sprintf(h_xid, "%x %x %x %x - %x %x %x %x",
                (unsigned char)info.xid.data[0],
                (unsigned char)info.xid.data[1],
                (unsigned char)info.xid.data[2],
                (unsigned char)info.xid.data[3],
                (unsigned char)info.xid.data[4],
                (unsigned char)info.xid.data[5],
                (unsigned char)info.xid.data[6],
                (unsigned char)info.xid.data[7]);

            printf("xid=[%s] \n", h_xid);

    } else {
        printf("Not in transaction \n");
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    EXEC SQL
    INSERT INTO tmax( empno, ename, job, hiredate,sal, xid)
    VALUES (:h_empno, :h_ename, :h_job, to_date(:h_date,'yymmdd'), :h_sal, :h_xid );

    if ( sqlca.sqlcode != SQLOK ) {
        sprintf(tmp, "[%s] ORACLE tmax Insert Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] ORACLE tmax Insert Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_2_oracle( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    h_empno = h_sal = 0;

    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );


    EXEC SQL UPDATE tmax
        SET     empno = :h_empno+1,
                ename = :h_ename,
                job   = 'UPDATE'
        WHERE   empno = :h_empno;

    if ( sqlca.sqlcode != SQLOK ) {
        sprintf(tmp, "[%s] ORACLE tmax Update Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] ORACLE tmax Update Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_3_oracle( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

    h_empno = h_sal = 0;
    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );

    EXEC SQL DELETE tmax
        WHERE   empno = :h_empno;

    if ( sqlca.sqlcode != SQLOK ) {
        sprintf(tmp, "[%s] ORACLE tmax Delete Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] ORACLE tmax Delete Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_4_oracle( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

    h_empno = h_sal = h_count = 0;
    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );


    EXEC SQL SELECT COUNT(*)
        INTO    :h_count
        FROM    tmax
        WHERE   empno = :h_empno;

    if ((sqlca.sqlcode != SQLOK) && (sqlca.sqlcode != SQLNOTFOUND)) {
        sprintf(tmp, "[%s] ORACLE tmax Select Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] ORACLE tmax Select Success [%d]", msg->name, h_count);
    strcpy(rcvbuf, tmp);
    return 0;
}
DB2 Call Function

The following program uses a function to call DB2.

#include <stdio.h>
#include <ctype.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>
#include "svr1.h"

EXEC SQL INCLUDE SQLCA;

EXEC SQL BEGIN DECLARE SECTION;
    sqlint32  h_empno;
    char h_ename[50];
    char h_job[50];
    char h_date[50];
    float h_sal;
    sqlint32  h_count;
    char h_xid[100];
EXEC SQL END DECLARE SECTION;

SVC2301TX_1_db2( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];
    TXINFO info;

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

    h_empno = h_sal = 0;
    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );
    memset( h_xid , 0x00, sizeof( h_xid  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );

    if (tx_info(&info)==1) {
            sprintf(h_xid, "%x %x %x %x - %x %x %x %x",
                (unsigned char)info.xid.data[0],
                (unsigned char)info.xid.data[1],
                (unsigned char)info.xid.data[2],
                (unsigned char)info.xid.data[3],
                (unsigned char)info.xid.data[4],
                (unsigned char)info.xid.data[5],
                (unsigned char)info.xid.data[6],
                (unsigned char)info.xid.data[7]);

            printf("xid=[%s] \n", h_xid);

    } else {
        printf("Not in transaction \n");
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    EXEC SQL
    INSERT INTO tmax( empno, ename, job, hiredate,sal, xid)
    VALUES (:h_empno, :h_ename, :h_job, to_date(:h_date,'yymmdd'), :h_sal, :h_xid );

    if (sqlca.sqlcode != SQLOK) {
        sprintf(tmp, "[%s] DB2 tmax Insert Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] DB2 tmax Insert Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_2_db2( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    h_empno = h_sal = 0;
    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );

    EXEC SQL UPDATE tmax
        SET     empno = :h_empno+1,
                ename = :h_ename,
                job   = 'UPDATE'
        WHERE   empno = :h_empno;

    if (sqlca.sqlcode != SQLOK) {
        sprintf(tmp, "[%s] DB2 tmax Update Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] DB2 tmax Update Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_3_db2( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

    h_empno = h_sal = 0;
    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );

    EXEC SQL DELETE tmax
        WHERE   empno = :h_empno;

    if (sqlca.sqlcode != SQLOK) {
        sprintf(tmp, "[%s] DB2 tmax Delete Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] DB2 tmax Delete Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_4_db2( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    h_empno = h_sal = h_count = 0;
    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );

    EXEC SQL SELECT COUNT(*)
        INTO    :h_count
        FROM    tmax
        WHERE   empno = :h_empno;

    if ((sqlca.sqlcode != SQLOK) && (sqlca.sqlcode != SQLNOTFOUND)) {
        sprintf(tmp, "[%s] DB2 tmax Select Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] DB2 tmax Select Success [%d]", msg->name, h_count);
    strcpy(rcvbuf, tmp);
    return 0;
}

Since MultipleRM does not support xa dynamic registration, tx_begin() must always be called even when executing a SELECT statement for business purposes. Alternatively, the service handling the SELECT statement must be separated into a dedicated server that does not include services performing INSERT, UPDATE, or DELETE operations to ensure proper operation.

3.2. Environment Configuration and Compilation

The following shows the environment configuration and compilation of MultipleRM that uses Oracle and DB2.

Environment Configuration

The following is a MultipleRM server configuration example.

*DOMAIN
dom1    SHMKEY =@SHMEMKY@,    MINCLH=1, MAXCLH=1,
        TPORTNO=@TPORTNO@,    BLOCKTIME=60,
        RACPORT = @TRACPORT@,
        MAXTMS=50

*NODE
@HOSTNAME@    TMAXDIR="@TMAXDIR@",
        APPDIR="@TMAXDIR@/appbin",
        PATHDIR = "@TMAXDIR@/path",
        TLOGDIR = "@TMAXDIR@/log/tlog",
        ULOGDIR="@TMAXDIR@/log/ulog",
        SLOGDIR="@TMAXDIR@/log/slog",

*SVRGROUP
svg12301X    NODENAME = @HOSTNAME@,
        DBNAME = ORACLE,
        OPENINFO="Oracle_Xa+Acc=P/scott/tiger+SesTm=60+DbgFl=7+LogDir=@TMAXDIR@/log/xalog",
        TMSNAME = tms_ora,
        SVGTYPE = STMAX

svg12302X    NODENAME = @HOSTNAME@,
        DBNAME = DB2_STATIC,
        OPENINFO = "db=tptest,uid=tmaxha,pwd=ha0115",
        TMSNAME = tms_db2,
        SVGTYPE = STMAX

svgm1    NODENAME = @HOSTNAME@,
        SVGLIST = "svg12301X, svg12302X",
        SVGTYPE = MTMAX

*SERVER
# Common Insert/Update/Delete/Select
svr2301TX       SVGNAME = svgm1, MIN=5, MAX=5, MAXRSTART=-1
svr2301TX_UCS       SVGNAME = svgm1, MIN=5, MAX=5, MAXRSTART=-1, SVRTYPE=UCS

# Server Transaction
svr2311TX       SVGNAME = svg12301X

# tpforward
svr2321TX       SVGNAME = svg12301X
svr2322TX       SVGNAME = svg12301X

# AUTOTRAN
svr2331TX       SVGNAME = svg12301X

*SERVICE
# Common Insert/Update/Delete/Select
SVC2301TX_1     SVRNAME = svr2301TX, SVCTIME=5
SVC2301TX_2     SVRNAME = svr2301TX, SVCTIME=5
SVC2301TX_3     SVRNAME = svr2301TX, SVCTIME=5
SVC2301TX_4     SVRNAME = svr2301TX, SVCTIME=5
SVC2301TX_1_UCS     SVRNAME = svr2301TX_UCS, SVCTIME=5
SVC2301TX_2_UCS     SVRNAME = svr2301TX_UCS, SVCTIME=5
SVC2301TX_3_UCS     SVRNAME = svr2301TX_UCS, SVCTIME=5
SVC2301TX_4_UCS     SVRNAME = svr2301TX_UCS, SVCTIME=5
Makefile

-lnodb must be included in the LIBS item of the makefile. The following is a makefile that compiles a MultipleRM server program on 64-bit Linux.

# Oracle Env
include $(ORACLE_HOME)/precomp/lib/env_precomp.mk
ORALIBDIR = $(LIBHOME)
ORALIB = $(PROLDLIBS)

# DB2 Env
DB2LIBDIR = $(DB2_HOME)/lib
DB2LIBS   = -ldb2

DB      = TPTEST
DB2USER = tmaxha
DB2PASS = ha0115


# Server makefile

TARGET    = $(COMP_TARGET)
APOBJS    = $(TARGET).o
NSDLOBJ = $(TMAXDIR)/lib64/sdl.o
AP_ORA_OBJS = $(TARGET)_ora.o
AP_DB2_OBJS = $(TARGET)_db2.o

LIBS    = -lsvr  -lnsl -lnodb
OBJS    = $(AP_ORA_OBJS) $(AP_DB2_OBJS) $(APOBJS) $(SVCTOBJ)
SVCTOBJ = $(TARGET)_svctab.o

CFLAGS  =  -O -I$(TMAXDIR)

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
TMAXLIBDIR  = $(TMAXDIR)/lib64

#
.SUFFIXES : .c

.c.o:
    $(CC) $(CFLAGS) -c $<

#
# server compile
#
all: $(TARGET)

$(TARGET): $(OBJS)
    echo $(ORALIBDIR)
    $(CC) $(CFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) -L$(DB2LIBDIR) -L$(ORALIBDIR) $(ORALIB) $(DB2LIBS) $(OBJS) $(LIBS) $(NSDLOBJ)
    mv $(TARGET) $(APPDIR)/.
    rm -f $(OBJS)

$(AP_ORA_OBJS):
    echo $(OBJS)
    proc iname=$(TARGET)_ora.pc include=$(TMAXDIR)
    $(CC) $(CFLAGS) -c $(TARGET)_ora.c

$(AP_DB2_OBJS):
    echo $(OBJS)
    db2 connect to $(DB) user $(DB2USER) using $(DB2PASS)
    db2 prep $(TARGET)_db2.sqc bindfile
    db2 bind $(TARGET)_db2.bnd
    db2 connect reset
    db2 terminate
    $(CC) $(CFLAGS) $(LDFLAGS)  -I$(DB2_HOME)/include -c $(TARGET)_db2.c

$(APOBJS): $(TARGET).c
    $(CC) $(CFLAGS) -c $(TARGET).c

$(SVCTOBJ):
    cp -f $(SVCTDIR)/$(TARGET)_svctab.c .
    touch ./$(TARGET)_svctab.c
    $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c

#
clean:
    -rm -f *.o core $(APPDIR)/$(TARGET)

Makefile contents may vary depending on the operating system.

4. Using Oracle and MQ

Oracle and MQ can be used with TCS using the server library. This section provides examples of programs that use Oracle and MQ, as well as a makefile example for configuration and compilation.

4.1. Program Examples

The following is a TCS example program that uses Oracle and MQ together. This program example can be written using embedded SQL and compiled using a precompiler provided by each database vendor.

MQ Call Function

The following program uses a function to call MQ.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmqc.h>
#include <getopt.h>
#include <usrinc/atmi.h>
#include <usrinc/tmaxapi.h>
#include <usrinc/fbuf.h>
#include <usrinc/tx.h>
#include "../sdl/demo_sdl.h"
#include "../fdl/demo_fdl.h"

#define MQ_CSP_PASSWORD_LENGTH         256

#define DATA_LEN 16


MQHCONN  Hcon;                   /* connection handle             */
MQHOBJ   Hobj;                   /* object handle                 */
char     QMName[50];             /* queue manager name            */
char     QName[50];              /* queue name                    */
MQOD     od = {MQOD_DEFAULT};    /* Object Descriptor             */
MQLONG   OpenCode = MQCC_FAILED; /* MQOPEN completion code        */
MQLONG   CReason;                /* reason code for MQCONNX       */

int      first;

int tpsvrinit(int argc, char **argv)
{
        /*   Declare MQI structures needed                                */
        MQCNO   cno = {MQCNO_DEFAULT};   /* connection options            */
        MQCSP   csp = {MQCSP_DEFAULT};   /* security parameters           */
        MQLONG   O_options;              /* MQOPEN options                */
        MQLONG   CompCode;               /* completion code               */
        MQLONG   Reason;                 /* reason code                   */
        char    *UserId = NULL;                 /* UserId for authentication     */
        char     Password[MQ_CSP_PASSWORD_LENGTH + 1] = {0}; /* For auth  */
        int                c;

        strcpy(QMName, "QM1");
        strcpy(QName, "Q1");

        opterr = 0;
        while ((c = getopt(argc, argv, "m:q:u:p:o:OE")) != EOF) {
                switch (c) {
                        case 'm':
                                strcpy(QMName, optarg);
                                break;
                        case 'q':
                                strcpy(QName, optarg);
                                break;
                        case 'u':
                                UserId = optarg;
                                break;
                        case 'p':
                                strcpy(Password, optarg);
                                break;
                        case 'O':
                                first = 0;
                                break;
                        case 'E':
                                first = 1;
                                break;
                        case '?':
                        default:
                                break;
                }
        }

        if (UserId != NULL) {
                cno.SecurityParmsPtr = &csp;
                cno.Version = MQCNO_VERSION_5;

                csp.AuthenticationType = MQCSP_AUTH_USER_ID_AND_PWD;
                csp.CSPUserIdPtr = UserId;
                csp.CSPUserIdLength = strlen(UserId);
                csp.CSPPasswordPtr = Password;
                csp.CSPPasswordLength = strlen(csp.CSPPasswordPtr);
        }

        MQCONNX(QMName,                 /* queue manager                  */
                &cno,                    /* connection options             */
                &Hcon,                   /* connection handle              */
                &CompCode,               /* completion code                */
                &CReason);               /* reason code                    */
        if (CompCode == MQCC_FAILED) {
                printf("MQCONNX ended with reason code %d\n", CReason);
                return -1;
        } else if (CompCode == MQCC_WARNING) {
                printf("MQCONNX generated a warning with reason code %d\n", CReason);
                printf("Continuing...\n");
        }

        strncpy(od.ObjectName, QName, (size_t)MQ_Q_NAME_LENGTH);
        printf("target queue is %s\n", od.ObjectName);

#if 0
        if (argc > 5)
        {
                strncpy(od.ObjectQMgrName, argv[5], (size_t) MQ_Q_MGR_NAME_LENGTH);
                printf("target queue manager is %s\n", od.ObjectQMgrName);
        }

        if (argc > 6)
        {
                strncpy(od.DynamicQName, argv[6], (size_t) MQ_Q_NAME_LENGTH);
                printf("dynamic queue name is %s\n", od.DynamicQName);
        }
#endif

        /*   Open the target message queue for output                     */
        O_options = MQOO_OUTPUT            /* open queue for output     */
                | MQOO_INPUT_AS_Q_DEF    /* open queue for input      */
                | MQOO_FAIL_IF_QUIESCING /* but not if MQM stopping   */
                ;                        /* = 0x2010 = 8208 decimal   */

        MQOPEN(Hcon,                      /* connection handle            */
                &od,                       /* object descriptor for queue  */
                O_options,                 /* open options                 */
                &Hobj,                     /* object handle                */
                &OpenCode,                 /* MQOPEN completion code       */
                &Reason);                  /* reason code                  */
        if (Reason != MQRC_NONE) {
                printf("MQOPEN ended with reason code %d\n", Reason);
        }
        if (OpenCode == MQCC_FAILED) {
                printf("unable to open queue for output\n");
        }
        return 0;
}

int tpsvrdone()
{
        MQLONG   C_options;              /* MQCLOSE options               */
        MQLONG   CompCode;               /* completion code               */
        MQLONG   Reason;                 /* reason code                   */

        /*   Close the target queue (if it was opened)                    */
        if (OpenCode != MQCC_FAILED) {
                C_options = MQCO_NONE;    /* no close options             */
                MQCLOSE(Hcon,             /* connection handle            */
                        &Hobj,            /* object handle                */
                        C_options,
                        &CompCode,        /* completion code              */
                        &Reason);         /* reason code                  */

                /* report reason, if any     */
                if (Reason != MQRC_NONE) {
                        printf("MQCLOSE ended with reason code %d\n", Reason);
                }
        }

        /*   Disconnect from MQM if not already connected                 */
        if (CReason != MQRC_ALREADY_CONNECTED) {
                MQDISC(&Hcon,              /* connection handle            */
                        &CompCode,         /* completion code              */
                        &Reason);          /* reason code                  */

                /* report reason, if any     */
                if (Reason != MQRC_NONE) {
                        printf("MQDISC ended with reason code %d\n", Reason);
                }
        }
        return 0;
}

INSERT_MQ(TPSVCINFO *msg)
{
        MQMD     md = {MQMD_DEFAULT};    /* Message Descriptor            */
        MQPMO   pmo = {MQPMO_DEFAULT};   /* put message options           */
        MQLONG   CompCode;               /* completion code               */
        MQLONG   Reason;                 /* reason code                   */

        memcpy(md.Format,           /* character string format            */
                MQFMT_STRING, (size_t)MQ_FORMAT_LENGTH);

        pmo.Options = MQPMO_SYNCPOINT
                | MQPMO_FAIL_IF_QUIESCING;
        /* pmo.Options |= MQPMO_NEW_MSG_ID;                               */
        /* pmo.Options |= MQPMO_NEW_CORREL_ID;                            */

        memcpy(md.MsgId, MQMI_NONE, sizeof(md.MsgId) );

        MQPUT(Hcon,                /* connection handle               */
                Hobj,              /* object handle                   */
                &md,               /* message descriptor              */
                &pmo,              /* default options (datagram)      */
                msg->len,          /* message length                  */
                msg->data,         /* message buffer                  */
                &CompCode,         /* completion code                 */
                &Reason);          /* reason code                     */
        if (Reason != MQRC_NONE) {
                printf("MQPUT ended with reason code %d\n", Reason);
        }
        if (CompCode != MQCC_FAILED)
                tpreturn(TPSUCCESS, 0, msg->data, msg->len, 0);
        else
                tpreturn(TPFAIL, 0, msg->data, msg->len, 0);
}

INSERT_DB(TPSVCINFO *msg)
{
        MQMD     md = {MQMD_DEFAULT};    /* Message Descriptor            */
        MQGMO   gmo = {MQGMO_DEFAULT};   /* get message options           */
        MQLONG   messlen;                /* message length                */
        MQLONG   CompCode;               /* completion code               */
        MQLONG   Reason;                 /* reason code                   */
        char *rcvbuf;
        long rcvlen;

        rcvbuf = tpalloc("STRING", NULL, 50000);
        if (rcvbuf == NULL)
                tpreturn(TPFAIL, 0, NULL, 0, 0);

        /*gmo.Version = MQGMO_VERSION_2;*/ /* Avoid need to reset Message */
        /*gmo.MatchOptions = MQMO_NONE; */ /* ID and Correlation ID after */
        /* every MQGET                 */
        gmo.Options = MQGMO_WAIT        /* wait for new messages       */
                | MQPMO_SYNCPOINT
                | MQGMO_CONVERT;        /* convert if necessary        */
        gmo.WaitInterval = 15000;       /* 15 second limit for waiting */

        rcvlen = 50000 -1; /* buffer size available for GET   */

        memcpy(md.MsgId, MQMI_NONE, sizeof(md.MsgId));
        memcpy(md.CorrelId, MQCI_NONE, sizeof(md.CorrelId));
        md.Encoding       = MQENC_NATIVE;
        md.CodedCharSetId = MQCCSI_Q_MGR;

        MQGET(Hcon,                /* connection handle                 */
                Hobj,              /* object handle                     */
                &md,               /* message descriptor                */
                &gmo,              /* get message options               */
                rcvlen,            /* buffer length                     */
                rcvbuf,            /* message buffer                    */
                &messlen,          /* message length                    */
                &CompCode,         /* completion code                   */
                &Reason);          /* reason code                       */

        /* report reason, if any    */
        if (Reason != MQRC_NONE) {

                if (Reason == MQRC_NO_MSG_AVAILABLE) {  /* special report for normal end */
                        printf("no more messages\n");
                } else {                      /* general report for other reasons */
                        printf("MQGET ended with reason code %d\n", Reason);

                        /*   treat truncated message as a failure for this sample   */
                        if (Reason == MQRC_TRUNCATED_MSG_FAILED) {
                                CompCode = MQCC_FAILED;
                        }
                }
        }

        if (CompCode != MQCC_FAILED) {
                msg->data = rcvbuf;
                msg->len = rcvlen;
                INSERT_ESQL(msg);
                tpreturn( TPSUCCESS, 0, NULL, 0, 0 );
        } else {
                tpreturn(TPFAIL, 0, NULL, 0, 0);
        }
}
Oracle Call Function

The following program uses a function to call Oracle.

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <usrinc/atmi.h>

EXEC SQL include sqlca.h;

EXEC SQL begin declare section;
        int  h_empno;
        char h_ename[11];
        char h_job[10];
        int  h_mgr;
        char h_date[11];
        float h_sal;
        float h_comm;
        int  h_deptno;
EXEC SQL end declare section;

INSERT_ESQL( TPSVCINFO *msg )
{
        int i;

        h_empno = h_mgr = h_sal = h_comm = h_deptno = 0;

        memset( h_ename, 0x00, sizeof( h_ename ) );
        memset( h_job, 0x00, sizeof( h_job ) );
        memset( h_date, 0x00, sizeof( h_date ) );

        strncpy(h_job, msg->name, 9);
        strncpy(h_ename, msg->data, 10);
        for(i = 0; i < sizeof(h_ename); i++)
                h_ename[i] = toupper(h_ename[i]);
        strcpy(h_date, "160101");
        h_empno = time(NULL) % 10000;

        EXEC SQL DECLARE ESQL DATABASE;

        EXEC SQL AT ESQL INSERT
        INTO emp( empno, ename, job, hiredate)
        VALUES ( :h_empno, :h_ename, :h_job, to_date(:h_date,'yymmdd'));

        if (sqlca.sqlcode != 0){
                printf( "insert failed sqlcode = %d\n",sqlca.sqlcode );
                tpreturn( TPFAIL, -1, NULL, 0, 0 );
        }

        printf("INSERT_ESQL insert success\n");
        if (!strcmp(msg->name, "INSERT_ESQL"))
                tpreturn( TPSUCCESS, 0, NULL, 0, 0 );
}

4.2. Environment Configuration and Compilation

The following shows the environment configuration and compilation of MultipleRM that uses Oracle and MQ.

Environment Configuration

The following is a MultipleRM server configuration example.

*DOMAIN
tmax1           SHMKEY=87789,
                MINCLH=1,
                MAXCLH=1,
                TPORTNO=7789,
                BLOCKTIME=5,
                TXTIME=10,
                RACPORT=3378,
                MAXCPC = 150
*NODE
qpsx2      TMAXDIR = "/home/tmax2/tmax",
                APPDIR  = "/home/tmax2/tmax/appbin",
                PATHDIR = "/home/tmax2/tmax/path",
                TLOGDIR = "/home/tmax2/tmax/log/tlog",
                ULOGDIR = "/home/tmax2/tmax/log/ulog",
                SLOGDIR = "/home/tmax2/tmax/log/slog"

*SVRGROUP
svg_mq           NODENAME = "qpsx2", DBNAME = MQ,
                 OPENINFO = "QMNAME=MQTEST",
                 TMSNAME = tms_mq, SVGTYPE = STMAX, RMID = 222,
                 MINTMS=1

svg_eora         NODENAME = "qpsx2", DBNAME = ORACLE,
                 OPENINFO = "Oracle_XA+Acc=P/scott/tiger+SesTm=60+LogDir=/home/tmax2/tmax/log/ulog+DbgFl=0x7+DB=ESQL",
                 TMSNAME = tms_ora, SVGTYPE = STMAX, RMID = 333,
                 MINTMS=1

svg_xa           NODENAME = "qpsx2", SVGTYPE = MTMAX,
                 SVGLIST = "svg_mq,svg_eora"

*SERVER
mrm_ora_mq       SVGNAME = svg_xa, CLOPT = "-- -m MQTEST -q ORANGE.LOCAL.QUEUE -E",
                 MAXRSTART = -1

*SERVICE
# Common PUT/GET
INSERT_DB        SVRNAME = mrm_ora_mq
INSERT_MQ        SVRNAME = mrm_ora_mq
Makefile

-lnodb must be included in the LIBS item of the makefile. The following is a makefile that compiles a MultipleRM server program on 32-bit Linux.

# Oracle Env
include $(ORACLE_HOME)/precomp/lib/env_precomp.mk
ORALIBDIR = $(LIBHOME)
ORALIB = $(PROLDLIBS)

LIBDIR = lib32

# Server makefile

TARGET        = $(COMP_TARGET)
TARGET2        = $(COMP_TARGET2)
APOBJS        = $(TARGET).o
APOBJS2        = $(TARGET2).o
NSDLOBJ = $(TMAXDIR)/lib/sdl.o

MQLIBD  = $(MQ_HOME)/lib
#MQLIB = -lmqm                # Server for C
#MQLIB = -lmqm_r        # Server for C for threaded
#MQLIB = -lmqmxa        # Server XA interface
#MQLIB = -lmqmxa_r        # Server XA interface for threaded
LIBS        = -lsvr  -lnsl -lnodb -lmqs -lmqmxa_r -lmqm_r -lm
OBJS    = $(APOBJS) $(APOBJS2) $(SVCTOBJ)
SVCTOBJ = $(TARGET)_svctab.o

CFLAGS  =  -v -m32 -O -I$(TMAXDIR) -I$(MQ_HOME)/inc

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
TMAXLIBDIR  = $(TMAXDIR)/lib

#
.SUFFIXES : .c

.c.o:
        $(CC) $(CFLAGS) -c $<

#
# server compile
#
all: $(TARGET)

$(TARGET): $(OBJS)
        $(CC) $(CFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) -L$(MQLIBD) -L$(ORALIBDIR) $(ORALIB)  $(OBJS) $(LIBS) $(NSDLOBJ)
        mv $(TARGET) $(APPDIR)/.
        rm -f $(OBJS)

$(APOBJS2): $(TARGET2).pc
        proc iname=$(TARGET2) include=$(TMAXDIR)  define=__LINUX_ORACLE_PROC__
        $(CC) $(CFLAGS) -c $(TARGET2).c


$(SVCTOBJ):
        cp -f $(SVCTDIR)/$(TARGET)_svctab.c .
        touch ./$(TARGET)_svctab.c
        $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c

#
clean:
        -rm -f *.o core $(APPDIR)/$(TARGET)

Makefile contents may vary depending on the operating system.

TMS Makefile

The following is a makefile example to compile Oracle TMS on 32-bit Linux.

# TMS Makefile for Oracle
# Linux

include $(ORACLE_HOME)/precomp/lib/env_precomp.mk
ORALIBDIR = $(LIBHOME)
ORALIB = $(PROLDLIBS) $(LIBCLNTSH)
LIBDIR = lib32

TARGET  = tms_ora
APOBJ   = dumy.o

APPDIR        = $(TMAXDIR)/appbin
TMAXLIBD= $(TMAXDIR)/lib
TMAXLIBS= -ltms -loras
#TMAXLIBS= -ltmsd -lorasd

CFLAGS  = -g -v -m32
CFLAGS  =  -m32
LDFLAGS =
SYSLIBS =

all : $(TARGET)

$(TARGET): $(APOBJ)
        $(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) -L$(TMAXLIBD) $(TMAXLIBS) $(APOBJ) -L$(ORALIBDIR) $(ORALIB) $(SYSLIBS)
        mv $(TARGET) $(APPDIR)/.

$(APOBJ):
        $(CC) $(CFLAGS) -c dumy.c
#
clean:
        -rm -f *.o core $(APPDIR)/$(TARGET)

The following is a makefile example to compile MQ TMS on 32-bit Linux.

# TMS Makefile for MQ
# Linux

MQLIBDIR = $(MQ_HOME)/lib
#MQLIB = -lmqmxa        # Server XA interface
MQLIB = -lmqmxa_r        # Server XA interface for threaded

TARGET  = tms_mq
APOBJ   = dumy.o

APPDIR  = $(TMAXDIR)/appbin
TMAXLIBD= $(TMAXDIR)/lib
TMAXLIBS= -ltms -lmqs

CFLAGS  = -m32
LDFLAGS =
SYSLIBS =

all : $(TARGET)

$(TARGET): $(APOBJ)
        $(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) -L$(TMAXLIBD) $(TMAXLIBS) $(APOBJ) -L$(MQLIBDIR) $(MQLIB)
        mv $(TARGET) $(APPDIR)/.

$(APOBJ):
        $(CC) $(CFLAGS) -c dumy.c
#
clean:
        -rm -f *.o core $(APPDIR)/$(TARGET)

5. Using Two Tibero Instances

Two Tibero instances can be used in TCS through the server library. This section describes examples of programs that use two Tibero instances, environment configuration, and makefile for compilation.

5.1. Program Examples

The following example shows transaction processing on a single server that uses two Tibero RM instances.

Since the same Tibero client library is used, the user must specify the instance to use when executing a query from user code. To achieve this, Tibero allows obtaining and specifying the corresponding instance connection using the ESQL statement (EXEC SQL XA SET CONNECTION AT: conn_id).

Service Execution

The following is program source code that executes a service.

#include <stdio.h>
#include <ctype.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>

extern int SVC2301TX_1_tibero(TPSVCINFO *msg);

SVC2301TX_1( TPSVCINFO *msg )
{
     int ret;
     int fail = 0;
     char *rcvbuf;

     rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
     if(rcvbuf==NULL) {
           printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
           tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

     ret = SVC2301TX_1_tibero(msg);
     if (ret < 0) {
          fail = 1;
     }
     if ( fail ) {
          printf("SVC2301TX_1  FAIL\n");
          tpreturn(TPFAIL,-1,NULL, 0,0);
     }

     strcpy(rcvbuf, "Multiple RM Insert Success");
     tpreturn( TPSUCCESS, 0, rcvbuf, strlen(rcvbuf), 0 );
}
Tibero Call Function

The following program uses a function to call Tibero.

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <usrinc/atmi.h>
#include <usrinc/tmaxapi.h>
#include <usrinc/fbuf.h>
#include <usrinc/tx.h>
#include "../sdl/demo_sdl.h"
#include "../fdl/demo_fdl.h"

#define SQLOK       0
#define SQLDUP      1
#define SQLNOTFOUND 1403

EXEC SQL INCLUDE SQLCA.H;
EXEC SQL BEGIN DECLARE SECTION;
     int  h_empno;
     char h_ename[MAXLEN];
     char h_job[MAXLEN];
     char h_date[MAXLEN];
     float h_sal;
     int  h_count;
     char h_xid[100];
     char h_conn_id[MAXLEN];
EXEC SQL END DECLARE SECTION;

SVC2301TX_1_tibero( TPSVCINFO *msg )
{
     str  sndbuf;
     char *rcvbuf;
     char tmp[1024];
     TXINFO info;

     sndbuf = (str)msg->data;
     rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
     if(rcvbuf==NULL) {
           printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
           tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

     h_empno = h_sal = 0;
     memset( h_ename, 0x00, sizeof( h_ename ) );
     memset( h_job, 0x00, sizeof( h_job ) );
     memset( h_date, 0x00, sizeof( h_date ) );
     memset( tmp , 0x00, sizeof( tmp  ) );
     memset( h_xid , 0x00, sizeof( h_xid  ) );
     memset( h_conn_id, 0x00, sizeof( h_conn_id ) );
     h_empno = sndbuf->empno;
     h_sal   = sndbuf->sal;
     strcpy( h_ename, sndbuf->ename );
     strcpy( h_job  , sndbuf->job   );
     strcpy( h_date , sndbuf->date  );

     if (tx_info(&info)==1) {
          sprintf(h_xid, "%x %x %x %x - %x %x %x %x",
          (unsigned char)info.xid.data[0],
          (unsigned char)info.xid.data[1],
          (unsigned char)info.xid.data[2],
          (unsigned char)info.xid.data[3],
          (unsigned char)info.xid.data[4],
          (unsigned char)info.xid.data[5],
          (unsigned char)info.xid.data[6],
          (unsigned char)info.xid.data[7]);

          printf("xid=[%s] \n", h_xid);
     } else {
          printf("Not in transaction \n");
          tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

     /* Set Default SQL CTX with conn_id */
     strcpy(h_conn_id, "DB1");

     EXEC SQL XA SET CONNECTION AT :h_conn_id;
     if (sqlca.sqlcode != SQLOK) {
          sprintf(tmp, "[%s] emp Insert Fail", msg->name);
          strcpy(rcvbuf, tmp);
          printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
          tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
     }

     EXEC SQL
     INSERT INTO tmax( empno, ename, job, hiredate,sal, xid)
     VALUES (:h_empno, :h_ename, :h_job, to_date(:h_date,'yymmdd'), :h_sal, :h_xid );
     if ( sqlca.sqlcode != SQLOK )
     {
         sprintf(tmp, "[%s] TIBERO tmax Insert Fail", msg->name);
         strcpy(rcvbuf, tmp);
         printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
         tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
     }

     sleep(1);

     /* Set Default SQL CTX with conn_id */
     strcpy(h_conn_id, "DB2");

     EXEC SQL XA SET CONNECTION AT :h_conn_id;
     if (sqlca.sqlcode != SQLOK)  {
          sprintf(tmp, "[%s] emp Insert Fail", msg->name);
          strcpy(rcvbuf, tmp);
          printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
          tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
     }

     EXEC SQL
     INSERT INTO tmax( empno, ename, job, hiredate,sal, xid)
     VALUES (:h_empno, :h_ename, :h_job, to_date(:h_date,'yymmdd'), :h_sal, :h_xid );
     if ( sqlca.sqlcode != SQLOK )
     {
          sprintf(tmp, "[%s] TIBERO tmax Insert Fail", msg->name);
          strcpy(rcvbuf, tmp);
          printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
          tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
     }

     sprintf(tmp, "[%s] TIBERO tmax Insert Success", msg->name);
     strcpy(rcvbuf, tmp);
     return 0;
}

5.2. Environment Configuration and Compilation

The following shows the environment configuration and compilation of MultipleRM that uses two Tibero instances.

Environment Configuration

The following is a MultipleRM server configuration example.

*DOMAIN
domain       SHMKEY   = 81522, MAXUSER = 10000,
             MINCLH   = 1, MAXCLH = 1,
             TPORTNO  = 11500,  BLOCKTIME = 30,
             MAXSACALL = 1024, MAXCACALL = 1024,
             MAXMTMAX = 3, MAXSTMAX = 10

*NODE
node1        TMAXDIR = "/home/tmax1/tmax",
             APPDIR  = "/home/tmax1/tmax/appbin",
             PATHDIR = "/home/tmax1/tmax/path",
             TLOGDIR = "/home/tmax1/tmax/log/tlog",
             ULOGDIR = "/home/tmax1/tmax/log/ulog",
             SLOGDIR = "/home/tmax1/tmax/log/slog"

node2        TMAXDIR = "/home/tmax2/tmax",
             APPDIR  = "/home/tmax2/tmax/appbin",
             PATHDIR = "/home/tmax2/tmax/path",
             TLOGDIR = "/home/tmax2/tmax/log/tlog",
             ULOGDIR = "/home/tmax2/tmax/log/ulog",
             SLOGDIR = "/home/tmax2/tmax/log/slog"

*SVRGROUP
svg_s1       NODENAME = node1,
             DBNAME = TIBERO,
             OPENINFO="TIBERO_XA:user=tibero,pwd=tmax,db=tibero1,Loose_Coupling=false,sestm=60,conn_id=DB1",
             TMSNAME = tms_tbr,
             SVGTYPE = STMAX,
             RMID = 1
svg_s2       NODENAME = node2,
             DBNAME = TIBERO,
             OPENINFO="TIBERO_XA:user=tibero,pwd=tmax,db=tibero2,Loose_Coupling=false,sestm=60,conn_id=DB2",
             TMSNAME = tms_tbr,
             SVGTYPE = STMAX,
             RMID = 2

svgm1        NODENAME = node1,
             SVGLIST = "svg_s1, svg_s2",
             SVGTYPE = MTMAX

*SERVER
svr2301TX    SVGNAME = svgm1, MIN=5, MAX=5, MAXRSTART=-1

*SERVICE
SVC2301TX_1  SVRNAME = svr2301TX, SVCTIME=5
Makefile

-lnodb must be included in the TMAXLIBS item of the makefile. The following is a makefile that compiles a MultipleRM server program on 64-bit Linux.

# Tibero Env
TBINC = $(TB_HOME)/client/include
TBLIBDIR = $(TB_HOME)/client/lib
TBLIB = -ltbxa -ltbertl -ltbcli -lm -lpthread

# Server makefile
TARGET = $(COMP_TARGET)
APOBJS = $(TARGET).o
NSDLOBJ = $(TMAXDIR)/lib64/sdl.o
AP_TBR_OBJS = $(TARGET)_tbr.o

LIBS = -lsvr  -lnsl -lnodb
OBJS    = $(AP_TBR_OBJS) $(APOBJS) $(SVCTOBJ)
SVCTOBJ = $(TARGET)_svctab.o
CFLAGS  =  -O -I$(TMAXDIR)

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
TMAXLIBDIR  = $(TMAXDIR)/lib64

#
.SUFFIXES : .c

.c.o:
    $(CC) $(CFLAGS) -c $<

#
# server compile
#
all: $(TARGET)

$(TARGET): $(OBJS)
    echo $(ORALIBDIR)
    $(CC) $(CFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) -L$(TBLIBDIR) $(TBLIB) $(OBJS) $(LIBS) $(NSDLOBJ)
    mv $(TARGET) $(APPDIR)/.
    rm -f $(OBJS)

$(AP_TBR_OBJS):
    echo $(OBJS)
    tbpc iname=$(TARGET)_tbr.tbc include=$(TMAXDIR)
    $(CC) $(CFLAGS) -I$(TBINC) -c $(TARGET)_tbr.c

$(APOBJS): $(TARGET).c
    $(CC) $(CFLAGS) -c $(TARGET).c

$(SVCTOBJ):
    cp -f $(SVCTDIR)/$(TARGET)_svctab.c .
    touch ./$(TARGET)_svctab.c
    $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c

#
clean:
    -rm -f *.o core $(APPDIR)/$(TARGET)

Makefile contents may vary depending on the operating system.