home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / sql / msdtc / odbc / odbc.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-03  |  13.7 KB  |  533 lines

  1.  
  2. /*    MS DTC Sample
  3. **
  4. **    This example uses MS DTC (distributed transaction
  5. **    coordinator) to perform simultaneous updates on two
  6. **    SQL servers.  The transaction in this example is 
  7. **    client initiated. The client also initiates the
  8. **    commit operation.
  9. **
  10. **  As this sample uses the ODBC interface, you'll need
  11. **  to configure ODBC data source names for 2 SQL servers
  12. **  that you can use to run this sample.  Each DSN; data
  13. **  source name,  must point to the pubs database in
  14. **  a separate SQL server. The authors table in the pubs
  15. **  database is used in this sample.
  16. **
  17. **  Build Instructions:
  18. **  This sample must link with the following files:  xolehlp.lib, odbc32.lib, odbccp32.lib
  19. **
  20. **  Usage Instructions:
  21. **  Please run this program with the '-h' flag.
  22. **
  23. */
  24.  
  25. //------------------------------------------------------------------------------
  26. // Include standard header files
  27. //------------------------------------------------------------------------------
  28. #include <windows.h>
  29. #include <stdio.h>
  30. #include <conio.h>
  31. #include <ctype.h>
  32.  
  33. //------------------------------------------------------------------------------
  34. // Include MS DTC specific header files.
  35. //------------------------------------------------------------------------------
  36. #define INITGUID
  37.  
  38. #include "txdtc.h"
  39. #include "xolehlp.h"
  40.  
  41.  
  42. //------------------------------------------------------------------------------
  43. // Include ODBC specific header file.
  44. //------------------------------------------------------------------------------
  45. #ifndef DBNTWIN32
  46. #define DBNTWIN32
  47.  
  48. #include <SQL.h>
  49. #include <SQLEXT.h>
  50. #include "odbcss.h"
  51. #include <ODBCINST.h>
  52.  
  53. #endif /* DBNTWIN32 */
  54.  
  55. //------------------------------------------------------------------------------
  56. // Define constants
  57. //------------------------------------------------------------------------------
  58. #define STR_LEN    40
  59.  
  60. //------------------------------------------------------------------------------
  61. // Define datatypes
  62. //------------------------------------------------------------------------------
  63. typedef struct DBCONN
  64. {
  65.     TCHAR    pszSrv    [STR_LEN];            // data source name, configured through control panel
  66.     TCHAR    pszUser [STR_LEN];            // Login user name
  67.     TCHAR    pszPasswd[STR_LEN];            // Login user password
  68.     HDBC    hdbc;                        // handle to an ODBC database connection
  69.     HSTMT    hstmt;                        // an ODBC statement handle, for use with SQLExecDirect
  70.  
  71. }    DBCONN;
  72.  
  73.  
  74. //------------------------------------------------------------------------------
  75. // Define Globals
  76. //------------------------------------------------------------------------------
  77. static DBCONN    gSrv1, gSrv2;            // global DB connection struct for server 1 and 2
  78.  
  79. static TCHAR    gAuthorID[STR_LEN];        // use 11 chars only - per Authors table.
  80. static TCHAR    gNewAddress[STR_LEN+1];    // max. address length in the Author's table.
  81.  
  82. //-------------------------------------------------------------------------------    
  83. //  ODBC specific global vars...
  84. //-------------------------------------------------------------------------------
  85. HENV            gHenv = SQL_NULL_HENV;
  86.  
  87. //-------------------------------------------------------------------------------    
  88. // Forward declaration of routines used.
  89. //-------------------------------------------------------------------------------    
  90.  
  91. void InitGlobals(int argc, char **argv);
  92. void LogonToDB(DBCONN *ptr);
  93. void Enlist(DBCONN *ptr, ITransaction *pTransaction);
  94. void ExecuteStatement(DBCONN *ptr, LPTSTR pszBuf);
  95. BOOL ProcessRC(LPTSTR pszFuncName, DBCONN *ptr, RETCODE rc);
  96. void DoSQLError(DBCONN *ptr);
  97. void FreeODBCHandles(DBCONN *ptr);
  98.  
  99.  
  100. //-------------------------------------------------------------------------------    
  101. // main()
  102. //-------------------------------------------------------------------------------    
  103.  
  104. void main(int argc, char **argv)
  105. {
  106.  
  107.     ITransactionDispenser    *pTransactionDispenser;
  108.     ITransaction            *pTransaction;
  109.     HRESULT                    hr = S_OK ;
  110.     BOOL                    tf = 0 ;
  111.     TCHAR                    SqlStatement[STR_LEN*2];
  112.     
  113.  
  114.     // Initialize globals & validate command line arguments
  115.     InitGlobals(argc,argv);
  116.  
  117.  
  118.     // Obtain the ITransactionDispenser Interface pointer
  119.     // by calling DtcGetTransactionManager()
  120.     hr = DtcGetTransactionManager(
  121.                 NULL,                                // LPTSTR     pszHost,
  122.                 NULL,                                // LPTSTR     pszTmName,
  123.                 IID_ITransactionDispenser,            // /* in  */ REFIID rid,
  124.                 0,                                    // /* in  */ DWORD    dwReserved1,
  125.                 0,                                     // /* in  */ WORD    wcbReserved2,
  126.                 NULL,                                // /* in  */ void FAR * pvReserved2,
  127.                 (void **)&pTransactionDispenser     // /* out */ void** ppvObject
  128.                 );
  129.  
  130.     if (FAILED (hr))
  131.     {
  132.         printf("DtcGetTransactionManager failed: %x\n", hr);
  133.            exit (1);
  134.     }
  135.  
  136.     // Initialize the ODBC environment handle.
  137.     if (SQL_NULL_HENV == gHenv)
  138.     {
  139.         ProcessRC("SQLAllocEnv",0,SQLAllocEnv(&gHenv));
  140.     }
  141.  
  142.     // Establish connection to database on server#1
  143.     LogonToDB(&gSrv1);
  144.                      
  145.     // Establish connection to database on server#2
  146.     LogonToDB(&gSrv2);
  147.  
  148.  
  149.     // Loop performing distributed transactions
  150.     for (INT i = 0; i < 5; i++)
  151.     {
  152.  
  153.         // Initiate an MS DTC transaction
  154.         hr = pTransactionDispenser->BeginTransaction( 
  155.                 NULL,                            //    /* [in]  */ IUnknown __RPC_FAR *punkOuter,
  156.                 ISOLATIONLEVEL_ISOLATED,        //    /* [in]  */ ISOLEVEL isoLevel,
  157.                 ISOFLAG_RETAIN_DONTCARE,        //     /* [in]  */ ULONG isoFlags,
  158.                 NULL,                            //    /* [in]  */ ITransactionOptions *pOptions 
  159.                 &pTransaction                    //    /* [out] */ ITransaction **ppTransaction
  160.                 ) ;
  161.             
  162.         if (FAILED (hr))
  163.         {    
  164.             printf("BeginTransaction failed: %x\n",hr);
  165.             exit(1);
  166.         }
  167.  
  168.         // Enlist each of the data sources in the transaction
  169.         Enlist(&gSrv1,pTransaction);
  170.         Enlist(&gSrv2,pTransaction);
  171.  
  172.         // Generate the SQL statement to execute on each of the databases
  173.         sprintf(SqlStatement,
  174.                 "update authors set address = '%s_%d' where au_id = '%s'",
  175.                  gNewAddress,i,gAuthorID);
  176.         
  177.         // Perform updates on both of the DBs participating in the transaction
  178.         ExecuteStatement(&gSrv1,SqlStatement);
  179.         ExecuteStatement(&gSrv2,SqlStatement);
  180.  
  181.         // Commit the transaction 
  182.         hr = pTransaction->Commit(0,0,0);
  183.         if (FAILED(hr))
  184.         {
  185.             printf("pTransaction->Commit() failed: %x\n",hr);
  186.             exit(1);
  187.         }
  188.  
  189.         // At end of each transaction, pTransaction-Release() must be called.
  190.         hr = pTransaction->Release();
  191.         if (FAILED(hr))
  192.         {
  193.             printf("pTransaction->Release() failed: %x\n",hr);
  194.             exit(1);
  195.         }
  196.  
  197.         printf("Successfully committed Transaction #%d\n",i);
  198.  
  199.     } // for 
  200.  
  201.     
  202.     // release the transaction dispenser    
  203.     pTransactionDispenser->Release();
  204.  
  205.     // Free ODBC handles
  206.     FreeODBCHandles(&gSrv1);
  207.     FreeODBCHandles(&gSrv2);
  208.  
  209.     // Free the global ODBC environment handle.
  210.     SQLFreeEnv(gHenv);
  211. }
  212.  
  213.  
  214.  
  215. //-------------------------------------------------------------------------------    
  216.  
  217. void InitGlobals(INT argc, char **argv)
  218. {
  219.  
  220.     TCHAR Usage[] = "\n           [-S1  server_1] \n"        \
  221.                     "           [-U1  user_name_1]\n"    \
  222.                     "           [-P1  passwd_1]\n"        \
  223.                     "           [-S2  server_2]\n"        \
  224.                     "           [-U2  user_name_2]\n"    \
  225.                     "           [-P2  passwd_2]\n"        \
  226.                     "           [-ID  au_id]\n"            \
  227.                     "           [-Ad  new_address\n"    \
  228.                     "           [-h   Usage]\n";
  229.  
  230.  
  231.     // Init ODBC handles to be invalid
  232.     gSrv1.hdbc = SQL_NULL_HDBC;
  233.     gSrv1.hstmt= SQL_NULL_HSTMT;
  234.  
  235.     gSrv2.hdbc = SQL_NULL_HDBC;
  236.     gSrv2.hstmt= SQL_NULL_HSTMT;
  237.  
  238.     // Null login info, set defaults as appropriate
  239.  
  240.     lstrcpy(gSrv1.pszSrv,"");
  241.     lstrcpy(gSrv1.pszUser,"sa");
  242.     lstrcpy(gSrv1.pszPasswd,"");
  243.     lstrcpy(gSrv2.pszSrv,"");
  244.     lstrcpy(gSrv2.pszUser,"sa");
  245.     lstrcpy(gSrv2.pszPasswd,"");
  246.  
  247.     lstrcpy(gAuthorID,"172-32-1176");    // default au_id value from authors table in pubs db.
  248.  
  249.     // scan command line arguments for user input.
  250.     for (INT i = 1; i < argc; i++)
  251.     {
  252.         if (*argv[i] == '-' || *argv[i] == '/')
  253.         {
  254.             switch (argv[i][1])
  255.             {
  256.                 // get the server or DSN name
  257.                 case 's':
  258.                 case 'S':
  259.                          switch (argv[i][2])
  260.                          {
  261.  
  262.                          case '1':
  263.                              lstrcpy(gSrv1.pszSrv,argv[++i]);
  264.                              break;
  265.  
  266.                          case '2':
  267.                              lstrcpy(gSrv2.pszSrv,argv[++i]);
  268.                              break;
  269.  
  270.                          default:
  271.                              printf("Invalid Input %s\n",argv[i]);
  272.                              printf("\nUsage: %s %s",argv[0],Usage);
  273.                              exit(1);
  274.                          };
  275.                          break;
  276.  
  277.                 // get user name
  278.                 case 'u':
  279.                 case 'U':
  280.                          switch (argv[i][2])
  281.                          {
  282.  
  283.                          case '1':
  284.                              lstrcpy(gSrv1.pszUser,argv[++i]);
  285.                              break;
  286.  
  287.                          case '2':
  288.                              lstrcpy(gSrv2.pszUser,argv[++i]);
  289.                              break;
  290.  
  291.                          default:
  292.                              printf("Invalid Input %s\n",argv[i]);
  293.                              printf("\nUsage: %s %s",argv[0],Usage);
  294.                              exit(1);
  295.                          };
  296.                          break;
  297.  
  298.                 // get password
  299.                 case 'p':
  300.                 case 'P':
  301.                          switch (argv[i][2])
  302.                          {
  303.  
  304.                          case '1':
  305.                              lstrcpy(gSrv1.pszPasswd,argv[++i]);
  306.                              break;
  307.  
  308.                          case '2':
  309.                              lstrcpy(gSrv2.pszPasswd,argv[++i]);
  310.                              break;
  311.  
  312.                          default:
  313.                              printf("Invalid Input %s\n",argv[i]);
  314.                              printf("\nUsage: %s %s",argv[0],Usage);
  315.                              exit(1);
  316.                           };
  317.                          break;
  318.                 
  319.                 // get au_id, overriding default value.
  320.                 case 'i':
  321.                 case 'I':
  322.                             lstrcpy(gAuthorID,argv[++i]);
  323.                             break;
  324.                 // get new address to associate with the au_id
  325.                 case 'a':
  326.                 case 'A':
  327.                             lstrcpy(gNewAddress,argv[++i]);
  328.                             break;
  329.                 case '?':
  330.                 case 'h':
  331.                 case 'H':
  332.                         printf("\nUsage: %s %s",argv[0],Usage);
  333.                         exit(1);
  334.                         break;
  335.                         
  336.                 default:
  337.                         printf("Invalid Input: %s\n",argv[i]);
  338.                         printf("\nUsage: %s %s",argv[0],Usage);
  339.                         exit(1);
  340.             }
  341.         }
  342.         else 
  343.         {
  344.             printf("Illegal command line argument #%d, %s\n",i,argv[i]);
  345.             printf("\nUsage: %s %s",argv[0],Usage);
  346.             exit(1);
  347.         }
  348.     }
  349.  
  350.     printf("-----------------------------\n");
  351.  
  352.     printf("MS DTC/ODBC Sample Configuration parameters\n");
  353.     printf( "server_1:      %s\n"            \
  354.             "user_name_1:   %s\n"            \
  355.             "passwd_1:      %s\n"            \
  356.             "server_2:      %s\n"            \
  357.             "user_name_2:   %s\n"            \
  358.             "passwd_2:      %s\n",
  359.  
  360.             gSrv1.pszSrv,gSrv1.pszUser,gSrv1.pszPasswd,
  361.             gSrv2.pszSrv,gSrv2.pszUser,gSrv2.pszPasswd);
  362.             
  363.     printf("-----------------------------\n");
  364.  
  365. }
  366.  
  367. //-------------------------------------------------------------------------------    
  368.  
  369. void LogonToDB(DBCONN *ptr)
  370. {
  371.     RETCODE rc = 0;
  372.  
  373.     rc = SQLAllocConnect(gHenv, &(ptr->hdbc) );
  374.  
  375.     if (ProcessRC("SQLAllocConnect",ptr,rc))
  376.     {
  377.         rc = SQLConnect(ptr->hdbc, 
  378.                         (UCHAR *)(ptr->pszSrv),
  379.                         SQL_NTS,
  380.                         (UCHAR *)(ptr->pszUser),
  381.                         SQL_NTS,
  382.                         (UCHAR *)(ptr->pszPasswd),
  383.                         SQL_NTS
  384.                         );
  385.  
  386.         ProcessRC("SQLConnect",ptr,rc);
  387.     }
  388. }
  389.  
  390. //-------------------------------------------------------------------------------    
  391.  
  392. void Enlist(DBCONN *ptr, ITransaction *pTransaction)
  393. {
  394.     RETCODE rc = 0;
  395.  
  396.     // Enlist database in the transaction
  397.  
  398.     rc = SQLSetConnectOption (ptr->hdbc, SQL_COPT_SS_ENLIST_IN_DTC, (UDWORD)pTransaction);
  399.  
  400.     ProcessRC("SQLSetConnectOption",ptr,rc);
  401. }
  402.  
  403. // ---------------------------------------------------------------------------
  404.  
  405. void ExecuteStatement(DBCONN *ptr, LPTSTR pszBuf)
  406. {
  407.     RETCODE rc = 0;
  408.  
  409.     // Allocate a statement handle for use with SQLExecDirect
  410.     rc = SQLAllocStmt(ptr->hdbc,&(ptr->hstmt));
  411.     
  412.     ProcessRC("SQLAllocStmt",ptr,rc);
  413.  
  414.     // Execute the passed string as a SQL statement
  415.     rc = SQLExecDirect(ptr->hstmt,(UCHAR *)pszBuf,SQL_NTS);
  416.     
  417.     ProcessRC("SQLExecDirect",ptr,rc);
  418.  
  419.     // Free the statement handle 
  420.     rc = SQLFreeStmt(ptr->hstmt, SQL_DROP);
  421.     ptr->hstmt = SQL_NULL_HSTMT;
  422.  
  423.     ProcessRC("SQLFreeStmt",ptr,rc);
  424.     
  425. }
  426.  
  427. // ---------------------------------------------------------------------------
  428.  
  429. void FreeODBCHandles(DBCONN *ptr)
  430. {
  431.     SQLDisconnect(ptr->hdbc);
  432.     
  433.     SQLFreeConnect(ptr->hdbc);
  434.     
  435.     ptr->hdbc   = SQL_NULL_HDBC;
  436.     ptr->hstmt  = SQL_NULL_HSTMT;
  437. }
  438.  
  439.  
  440. // ---------------------------------------------------------------------------
  441.  
  442. BOOL ProcessRC(LPTSTR pszFuncName, DBCONN *ptr,RETCODE rc)
  443. {
  444.  
  445.     switch (rc)
  446.     {
  447.         
  448.     case SQL_SUCCESS:
  449.             return TRUE;
  450.             break;
  451.  
  452.     case SQL_SUCCESS_WITH_INFO:
  453.             DoSQLError(ptr);
  454.             return TRUE;
  455.             break;
  456.  
  457.     case SQL_ERROR:
  458.             printf("%s Failed - see more info\n",pszFuncName);
  459.             DoSQLError(ptr);
  460.             exit(-1);
  461.             return FALSE;
  462.             break;
  463.  
  464.     case SQL_INVALID_HANDLE:
  465.             printf("%s Failed - SQL_INVALID_HANDLE\n",pszFuncName);
  466.             exit(-1);
  467.             return FALSE;
  468.             break;
  469.  
  470.     case SQL_NO_DATA_FOUND:
  471.             printf("%s Failed - SQL_NO_DATA_FOUND\n",pszFuncName);
  472.             return FALSE;
  473.             break;
  474.  
  475.     case SQL_STILL_EXECUTING:
  476.             printf("%s Failed - SQL_STILL_EXECUTING\n",pszFuncName);
  477.             return FALSE;
  478.             break;
  479.  
  480.     case SQL_NEED_DATA:
  481.             printf("%s Failed - SQL_NEED_DATA\n",pszFuncName);
  482.             return FALSE;
  483.             break;
  484.  
  485.     default:
  486.             printf("%s Failed - unexpected error, rc = %x\n",pszFuncName,rc);
  487.             DoSQLError(ptr);
  488.             exit(-1);
  489.             return FALSE;
  490.             break;
  491.     }
  492.  
  493. }
  494.  
  495. // ---------------------------------------------------------------------------
  496.  
  497. void DoSQLError(DBCONN *ptr)
  498. {
  499.  
  500.     const INT                MSG_BUF_SIZE = 300;
  501.     UCHAR                      szSqlState[MSG_BUF_SIZE];
  502.     UCHAR                    szErrorMsg[MSG_BUF_SIZE];
  503.     
  504.     SQLINTEGER    fNativeError    = 0;
  505.     SWORD        cbErrorMsg        = MSG_BUF_SIZE;
  506.     RETCODE        rc;
  507.  
  508.     rc = SQLError(gHenv,
  509.                   ptr ? ptr->hdbc : 0,
  510.                   ptr ? ptr->hstmt : 0,
  511.                   szSqlState,
  512.                   &fNativeError,
  513.                   szErrorMsg,
  514.                   MSG_BUF_SIZE,
  515.                   &cbErrorMsg
  516.                   );
  517.  
  518.     if (rc != SQL_NO_DATA_FOUND || rc != SQL_ERROR)
  519.     {
  520.         if (fNativeError != 0x1645)    // ignore change database to master context message
  521.         {
  522.             printf("SQLError info:\n");
  523.             printf("SqlState: %s, fNativeError: %x\n",szSqlState,fNativeError);
  524.             printf("Error Message: %s\n",szErrorMsg);
  525.         }
  526.     }
  527.     else
  528.     {    
  529.         printf("SQLError() failed: %x, NO_DATA_FOUND OR SQL_ERROR\n",rc);
  530.     }
  531.  
  532. }
  533. // ---------------------------------------------------------------------------