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 / ods / gateway / gatecbs.c next >
Encoding:
C/C++ Source or Header  |  1996-04-03  |  41.7 KB  |  1,289 lines

  1. // This file defines all the Open Data Services event handler functions for the
  2. // GATEWAY application.
  3. //
  4.  
  5. #include    <stdlib.h>
  6. #include    <stdio.h>
  7. #include    <string.h>
  8. #include    <windows.h>
  9. #include    <sqlfront.h>
  10. #include    <sqldb.h>
  11. #include    <srv.h>
  12.  
  13. // Define some user message codes.
  14. //
  15. #define SRV_MAXERROR    20000
  16. #define RPC_UNKNOWN     SRV_MAXERROR + 1
  17. #define COMPUTE_ROW     SRV_MAXERROR + 2
  18. #define REMOTE_MSG      SRV_MAXERROR + 3
  19. #define SEND_FAILED     SRV_MAXERROR + 4
  20. #define BULK_CMD        "insert bulk"
  21.  
  22. // This error value must be sent back to the client in the event
  23. // of a failure to login to the target database.  It is used in the
  24. // init_remote() function below.
  25. //
  26. #define REMOTE_FAIL 4002
  27.  
  28. // Declare the structure to use for our private data area.
  29. // It will be passed to the event handlers in the SRV_PROC structure.
  30. // It contains information to make and use a connection to the remote
  31. // DBMS -- in this case a SQL Server.
  32. //
  33. // A gateway to a non-SQL Server DBMS would contain a different structure.
  34. //
  35. #define MAXPARAMS   255
  36. typedef struct remote_dbms {
  37.     LOGINREC *login;    // The SQL Server login structure
  38.     DBPROCESS *dbproc;  // The connection to the remote SQL Server
  39.     BOOL bulk_mode;         // Flag indicating "bulk insert" mode
  40.     short retparams[MAXPARAMS]; // tag return parameters
  41. } REMOTE_DBMS;
  42.  
  43. // Declare the structure to be used for packing column metadata into the user datatype
  44. // field.  This is a new feature of SQL Server 6.0.  The additional column metadata
  45. // is available via dbcolinfo() in DB-Lib. 
  46.  
  47. typedef struct _typeinfo
  48. {
  49.     USHORT  typeid;            // used to hold the real user datatype
  50.     USHORT  nullable:1,     // TRUE/FALSE
  51.         case_sensitive:1,    // TRUE, FALSE
  52.         updateable:2,    // TRUE, FALSE, UNKNOWN
  53.         identity:1,        // TRUE/FALSE
  54.         spare1:1,        // spare fields
  55.         spare2:2,
  56.         spare3:8;
  57. } TYPEINFO;
  58.  
  59. // The remote server name of this gateway
  60. //
  61. DBCHAR *remote_server = NULL;
  62. SRV_PROC *Newsrvproc;       // Latest SRV_PROC allocated.
  63.  
  64. // Mutex used for init_remote()
  65. HANDLE init_remote_mutex = 0;
  66.  
  67. int dbcolntype(DBPROCESS *dbproc, int column);
  68.  
  69. int attn_handler(SRV_PROC *srvproc);
  70.  
  71. int chk_err(SRV_SERVER *server, SRV_PROC *srvproc, int srverror,
  72.             BYTE severity, BYTE state, int oserrnum, DBCHAR *errtext,
  73.             int errtextlen, DBCHAR *oserrtext, int oserrtextlen);
  74.  
  75. int init_remote(SRV_PROC *srvproc);
  76.  
  77. int init_server(SRV_SERVER *server);
  78.  
  79. int rpc_execute(SRV_PROC *srvproc);
  80.  
  81. int exit_remote(SRV_PROC *srvproc);
  82.  
  83. int lang_execute(SRV_PROC *srvproc);
  84.  
  85. int handle_results(DBPROCESS *rmtproc, SRV_PROC *srvproc);
  86.  
  87. int remotemsgs(DBPROCESS *dbproc, DBINT msgno, INT msgstate,
  88.                INT severity, LPCSTR msgtext, LPCSTR srvname, LPCSTR procname,
  89.                     DBUSMALLINT line);
  90.  
  91. int remoteerr(DBPROCESS *dbproc, INT severity, INT dberr, INT oserr,
  92.               LPCSTR dberrstr, LPCSTR oserrstr);
  93.  
  94. void set_remote_server_name(char *name);
  95.  
  96. //
  97. // This section defines the "call-back" functions supplied to the gateway
  98. // application.
  99. //
  100. #pragma check_stack( off )  // turn off stack checking
  101.  
  102. //
  103. // SET_REMOTE_SERVER_NAME
  104. //
  105. //     This function sets the name of the remote server.
  106. //
  107. // Parameters:
  108. //     name - Pointer to name of server.
  109. //
  110. // Returns:
  111. //     none
  112. //
  113. void set_remote_server_name(name)
  114. char *name;
  115. {
  116.     remote_server = name;
  117. }
  118.  
  119. //
  120. // INIT_SERVER
  121. //     Initialize the server on a SRV_START event.
  122. //     Event handlers for the server are installed.
  123. //
  124. // Parameters:
  125. //     server - Pointer to SRV_SERVER structure
  126. //
  127. // Returns:
  128. //     SRV_CONTINUE
  129. //
  130. int init_server(server)
  131. SRV_SERVER *server;
  132. {
  133.     char log_buffer[256];
  134.  
  135.     // When we get a connection request from a client, we want to
  136.     // call "init_remote()" to make a connection to the remote
  137.     // server.
  138.     //
  139.     srv_handle(server, (DBINT)SRV_CONNECT, init_remote);
  140.  
  141.     // When the client issues a language request, call
  142.     // "lang_execute()" to send the SQL statement to the remote DBMS.
  143.     //
  144.     srv_handle(server, (DBINT)SRV_LANGUAGE, lang_execute);
  145.  
  146.     // When the client issues an RSP, call "rpc_execute()"
  147.     // to send the RSP to the remote DBMS (the SQL Server).
  148.     //
  149.     srv_handle(server, (DBINT)SRV_RPC, rpc_execute);
  150.  
  151.     // When a disconnect request is issued, call "exit_remote()"
  152.     // to close the connection to the remote DBMS.
  153.     //
  154.     srv_handle(server, (DBINT)SRV_DISCONNECT, exit_remote);
  155.  
  156.     // Install the handler that will be called when the
  157.     // gateway receives an attention from one of its
  158.     // clients. 
  159.     //
  160.     srv_handle(server, (DBINT)SRV_ATTENTION, attn_handler);
  161.  
  162.      // Initialize dblib and configure for 100 connections
  163.      //
  164.      dbinit();
  165.      dbsetmaxprocs(100);
  166.  
  167.     // Now we'll install the message and error handlers for any
  168.     // messages from the remote DBMS.
  169.     //
  170.     dberrhandle(remoteerr);
  171.     dbmsghandle(remotemsgs);
  172.  
  173.      // Create mutex to serialize init_remote
  174.      //
  175.      init_remote_mutex = CreateMutex(NULL, FALSE, NULL);
  176.  
  177.     //  Log Server information to log file
  178.     //
  179.     sprintf(log_buffer, "Client connections allowed = %s",
  180.             srv_sfield(server, SRV_CONNECTIONS, (int *)NULL));
  181.  
  182.     srv_log(server, FALSE, log_buffer, SRV_NULLTERM);
  183.     printf("%s\n", log_buffer);
  184.  
  185.     return(SRV_CONTINUE);
  186. }
  187.  
  188. // INIT_REMOTE
  189. //    Event handler for a SRV_CONNECT event.
  190. //    A connection is made to the remote DBMS.
  191. //
  192. //    In this example program, the connection is to a SQL Server.
  193. //    If using a non-SQL Server remote DBMS, the connection code
  194. //    would be different, but would probably still occur here.
  195. //
  196. // Parameters:
  197. //      srvproc - the handle to the client connection that got the SRV_CONNECT.
  198. //
  199. // Returns:
  200. //     SRV_CONTINUE
  201. //
  202. // Side Effects:
  203. //     If the connection to the remote dbms cannot be made, then issue
  204. //     a SRV_DISCONNECT request.
  205. //
  206. //
  207. int init_remote(srvproc)
  208. SRV_PROC *srvproc;
  209. {
  210.      char *string;
  211.      int len;
  212.     REMOTE_DBMS *remote;    // the private data area to keep
  213.                                 // track of the remote DBMS
  214.                                 // connection.
  215.     BOOL    bImpersonated;
  216.  
  217.     // Set Newsrvproc.  This is used if we get an error on
  218.     // the open from DBLIB.  Since there isn't a dbproc,
  219.     // it is clear which srvproc to send the msg back on when the
  220.     // DBLIB error-handler gets called.
  221.     //
  222.     // First lock out any other threads trying to connect using this same
  223.     // function.
  224.     //
  225.     WaitForSingleObject(init_remote_mutex, INFINITE);
  226.  
  227.     Newsrvproc = srvproc;
  228.  
  229.      
  230.      if (SRV_TDSVERSION(srvproc) != SRV_TDS_6_0)          
  231.      {
  232.         // Send a message to the client that they must be 4.2 or 6.0 clients.  
  233.         // 4.2 client could be supported by 
  234.         // converting all decimal/numeric values to float before sending to
  235.         // client, and omiting the column metadata packed into srv_setutype in 
  236.         // the handle_results() function.
  237.         //
  238.         srv_sendmsg(srvproc, 
  239.                     SRV_MSG_ERROR, 
  240.                     (DBINT)REMOTE_FAIL, 
  241.                     (DBTINYINT)0,
  242.                     (DBTINYINT)0, 
  243.                     NULL, 
  244.                     0, 
  245.                     0, 
  246.                     "Gateway sample application only supports version 6.0 clients.",
  247.                     SRV_NULLTERM);
  248.  
  249.         // Now allow other threads to enter this function.
  250.         //
  251.        ReleaseMutex(init_remote_mutex);
  252.         return(SRV_DISCONNECT);
  253.     }
  254.  
  255.     // Allocate a REMOTE_DBMS information structure.
  256.     //
  257.     remote = (REMOTE_DBMS *)srv_alloc((DBINT)sizeof(*remote));
  258.  
  259.     // Try to open a connection to the remote DBMS.
  260.     //
  261.     if (remote == NULL) {
  262.         // Send a message to the client that
  263.         // the remote connection failed.
  264.         //
  265.         srv_sendmsg(srvproc, SRV_MSG_ERROR, (DBINT)REMOTE_FAIL, (DBTINYINT)0,
  266.                     (DBTINYINT)0, NULL, 0, 0,
  267.                     "Login to remote DBMS failed (srv_alloc).", SRV_NULLTERM);
  268.          ReleaseMutex(init_remote_mutex);
  269.         return(SRV_DISCONNECT);
  270.     }
  271.  
  272.     // Set "bulk insert" mode flag to false.
  273.     //
  274.     remote->bulk_mode = FALSE;
  275.  
  276.     // Allocate the LOGINREC structure used to make connections to the
  277.     // remote server. Open the connection in the SRV_CONNECT handler.
  278.     //
  279.     remote->login = dblogin();
  280.  
  281.     if (remote->login == NULL) {
  282.         // Send a message to the client that the
  283.         // remote connection failed.
  284.         //
  285.         srv_sendmsg(srvproc, SRV_MSG_ERROR, (DBINT)REMOTE_FAIL, (DBTINYINT)0,
  286.                     (DBTINYINT)0, NULL, 0, 0,
  287.                     "Login to remote DBMS failed (dblogin).", SRV_NULLTERM);
  288.  
  289.         // Deallocate the remote structure and set the user data
  290.         // pointer in "srvproc" to null so that the disconnect
  291.         // handler won't try to disconnect from the remote dbms.
  292.         //
  293.         srv_free(remote);
  294.          ReleaseMutex(init_remote_mutex);
  295.         return(SRV_DISCONNECT);
  296.     }
  297.     remote->dbproc = (DBPROCESS *)NULL;
  298.  
  299.     // Set the user name, password, and application name for the remote DBMS.
  300.     //
  301.     DBSETLUSER(remote->login, srv_pfield(srvproc, SRV_USER, (int *)NULL));
  302.     DBSETLPWD(remote->login, srv_pfield(srvproc, SRV_PWD, (int *)NULL));
  303.     DBSETLAPP(remote->login, srv_pfield(srvproc, SRV_APPLNAME, (int *)NULL));
  304.     DBSETLNATLANG(remote->login, srv_pfield(srvproc, SRV_NATLANG, (int *)NULL));
  305.  
  306.     // following will let us pass decimal data back to client
  307.     DBSETLVERSION(remote->login, DBVER60);
  308.  
  309.     //if client requested a trusted connection, gateway must do so too
  310.     if (srv_pfield(srvproc, SRV_LSECURE, (int *)NULL) == "TRUE")
  311.         DBSETLSECURE(remote->login);
  312.  
  313.     // Even if client hasn't explicitly requested a trusted connection, he may be
  314.     // using integrated security if the server is set to mixed or integrated mode.
  315.     // To handle this case, always tryy to impersonate the client.
  316.  
  317.     bImpersonated = srv_impersonate_client(srvproc);
  318.  
  319.     // See if client has set Bulk Copy flag
  320.     //
  321.     if (strcmp(srv_pfield(srvproc, SRV_BCPFLAG, (int *)NULL), "TRUE") == 0)
  322.         BCP_SETL(remote->login, TRUE);
  323.     else
  324.         BCP_SETL(remote->login, FALSE);
  325.  
  326.     // If no server name was specified as a parameter to the main program,
  327.     // then assume that the name is coming from the client.
  328.     //
  329.     if (remote_server == NULL || remote_server[0] == '\0') {
  330.         remote_server = srv_pfield(srvproc, SRV_HOST, (int *)NULL);
  331.     }
  332.    
  333.     // Try to open a connection to the remote DBMS.
  334.     //
  335.     if ((remote->dbproc = dbopen(remote->login,
  336.                                  remote_server)) == (DBPROCESS *)NULL) {
  337.  
  338.             // Send a message to the client that
  339.             // the remote connection failed.
  340.             //
  341.             srv_sendmsg(srvproc, 
  342.                     SRV_MSG_ERROR, 
  343.                     (DBINT)REMOTE_FAIL, 
  344.                     (DBTINYINT)0,
  345.                     (DBTINYINT)0, 
  346.                     NULL, 
  347.                     0, 
  348.                     0, 
  349.                     "Login to remote DBMS failed (dbopen).",
  350.                     SRV_NULLTERM);
  351.  
  352.         // Deallocate the remote structure and set the user data
  353.         // pointer in "srvproc" to null so that the disconnect
  354.         // handler won't try to disconnect from the remote DBMS.
  355.         //
  356.         srv_free(remote);
  357.         srv_setuserdata(srvproc, (BYTE *)NULL);
  358.          ReleaseMutex(init_remote_mutex);
  359.         if( bImpersonated )
  360.             srv_revert_to_self(srvproc);
  361.         return(SRV_DISCONNECT);
  362.     } else {
  363.         // Connection to the remote DBMS successful.  Save
  364.         // remote data structure in the "srvproc" so it will be
  365.         // available to the other handlers. We'll also map the remote
  366.         // DBMS connection to our "srvproc".
  367.         //
  368.         srv_setuserdata(srvproc, (VOID *)remote);
  369.         dbsetuserdata(remote->dbproc, (VOID *)srvproc);
  370.  
  371.     }
  372.  
  373.      ReleaseMutex(init_remote_mutex);
  374.  
  375.     // if currently running in user context, don't need to any more
  376.     if( bImpersonated )
  377.            srv_revert_to_self(srvproc);
  378.  
  379.     // Display connect info on console
  380.     //
  381.      string = srv_pfield(srvproc, SRV_CPID, &len);
  382.     string[len] = '\0';
  383.      printf("\nClient process ID: %s\n", string);
  384.  
  385.     string = srv_pfield(srvproc, SRV_USER, &len);
  386.     string[len] = '\0';
  387.     printf("User name: %s\n", string);
  388.  
  389.     string = srv_pfield(srvproc, SRV_APPLNAME, &len);
  390.     string[len] = '\0';
  391.     if (len > 0)
  392.        printf("Application program name: %s\n", string);
  393.  
  394.     string = srv_pfield(srvproc, SRV_RMTSERVER, &len);
  395.     string[len] = '\0';
  396.     if (len > 0)
  397.        printf("Remote Server: %s\n", string);
  398.  
  399.     return(SRV_CONTINUE);
  400. }
  401.  
  402. //
  403. // LANG_EXECUTE
  404. //     Execute a client language request on the remote dbms.
  405. //     Any results from the remote dbms are passed back to the client.
  406. //
  407. //  Parameters:
  408. //     srvproc -  process handle to the current client connection.
  409. //
  410. // Returns:
  411. //     SRV_CONTINUE
  412. //
  413. int lang_execute(srvproc)
  414. SRV_PROC *srvproc;
  415. {
  416.     REMOTE_DBMS *rmt_dbms;  // the remote database pointer
  417.     DBPROCESS *rmtproc; // our DBPROCESS pointer
  418.     DBCHAR *query;      // pointer to language buffer
  419.     int query_len;          // length of query
  420.     int status;             // status of dblib calls
  421.  
  422.     // Get the remote dbms pointer we saved in the srvproc via
  423.     // srv_setuserdata.
  424.     //
  425.     rmt_dbms = (REMOTE_DBMS *)srv_getuserdata(srvproc);
  426.  
  427.     // Get the pointer to the remote DBPROCESS
  428.     //
  429.     rmtproc = rmt_dbms->dbproc;
  430.  
  431.     // Get the pointer to the client language request command buffer.
  432.     //
  433.     query = srv_langptr(srvproc);
  434.  
  435.     // See if the previous command was a "bulk insert" command
  436.     //
  437.     if (rmt_dbms->bulk_mode) {
  438.  
  439.         // Get length of the SQL command.
  440.         //
  441.         query_len = srv_langlen(srvproc);
  442.  
  443.         // If length of data is zero, then send a zero length buffer
  444.         // to the destination SQL Server.  This is required in order to
  445.         // signal the SQL Server that no more data follows.
  446.         //
  447.         if (query_len == -1L)
  448.             query_len = 0L;
  449.  
  450.         // Place buffer into target SQL server's buffer.  Use
  451.         // dbfcmd() to pass all binary data from the language buffer on to
  452.         // the SQL Server.
  453.         //
  454.         status = dbfcmd(rmtproc, query, query_len);
  455.  
  456.         rmt_dbms->bulk_mode = FALSE;
  457.     } else {
  458.         // Let's check for "insert bulk" request
  459.         //
  460.         if (srv_langlen(srvproc) > (sizeof(BULK_CMD) - 1))
  461.             if (strnicmp(query, BULK_CMD, (sizeof(BULK_CMD) - 1)) == 0)
  462.                 rmt_dbms->bulk_mode = TRUE;
  463.  
  464.             // Place buffer into target SQL server's buffer.
  465.             //
  466.             status = dbcmd(rmtproc,
  467.             query);
  468.     }
  469.  
  470.     // If previous DBLIB call successful, send command buffer to SQL Server.
  471.     //
  472.     if (status == SUCCEED) {    // if previous DBLIB call successful
  473.         status = dbsqlexec(rmtproc);
  474.     }
  475.     if (!SRV_GOT_ATTENTION(srvproc) && (status == SUCCEED)) {
  476.         //
  477.         // Get the remote dbms results and pass them back to
  478.         // client.
  479.         //
  480.         handle_results(rmtproc, srvproc);
  481.     } else {
  482.         //
  483.         // If an attention event was issued or the dbsqlexec failed,
  484.         // acknowledge with senddone.
  485.         //
  486.         if (DBDEAD(rmtproc)) {
  487.             printf("thread shutting down");
  488.             srv_sendmsg(srvproc, SRV_MSG_ERROR, (DBINT)REMOTE_FAIL,
  489.                         (DBTINYINT)23, (DBTINYINT)17, NULL, 0, 0, "Remote Server connection no longer active: \
  490.                          thread shutting down"
  491.                         , SRV_NULLTERM);
  492.             srv_senddone(srvproc, SRV_DONE_FINAL | SRV_DONE_ERROR,
  493.                          (DBUSMALLINT)0, (DBINT)0);
  494.             srv_event(srvproc, SRV_DISCONNECT, NULL);
  495.         } else {
  496.             srv_senddone(srvproc, SRV_DONE_FINAL, (DBUSMALLINT)0, (DBINT)0);
  497.         }
  498.     }
  499.     return(SRV_CONTINUE);
  500. }
  501.  
  502. //
  503. // RPC_EXECUTE
  504. //     Execute a client RPC.
  505. //
  506. //     Note that in our example, the remote DBMS is an SQL Server.
  507. //     If our remote DBMS was a foreign DBMS, the code to send
  508. //     the query to the remote DBMS and receive remote results
  509. //     would be different.  In this example, we'll just send
  510. //     the rpc along to the SQL Server.
  511. //
  512. // Parameters:
  513. //    srvproc - The Open Server process handle to use to send results to the
  514. //               client.
  515. //
  516. // Returns:
  517. //     SRV_CONTINUE
  518. //
  519. int rpc_execute(srvproc)
  520. SRV_PROC *srvproc;
  521. {
  522.     DBPROCESS *rmtproc; // Our DBPROCESS pointer
  523.     int i;                  // Index variable
  524.     short params;
  525.     short retparam;
  526.     short *paramarray;
  527.     DBCHAR complete_rpc_name[4 * (MAXNAME + 1)]; //database.owner.name;#
  528.     DBCHAR *rpc_db;
  529.     DBCHAR *rpc_owner;
  530.     DBCHAR *rpc_name;
  531.     int rpc_number;
  532.     char rpc_number_char[17];
  533.     DBCHAR *rpc_paramname;
  534.     BYTE rpc_paramstatus;
  535.     int rpc_paramtype;
  536.     DBINT rpc_parammaxlen;
  537.     DBINT rpc_paramlen;
  538.     void *rpc_paramdata;
  539.     DBNUMERIC  numeric;        //structure to hold numeric params
  540.  
  541.     rmtproc = ((REMOTE_DBMS *)srv_getuserdata(srvproc))->dbproc;
  542.     paramarray = ((REMOTE_DBMS *)srv_getuserdata(srvproc))->retparams;    
  543.  
  544.     // initialize numeric structure
  545.     memset (&numeric, 0, sizeof(DBNUMERIC));
  546.  
  547.    // Construct full RPC name and initialize the RPC to the remote DBMS.
  548.    //
  549.     memset (complete_rpc_name, 0, sizeof(complete_rpc_name));
  550.     rpc_db = srv_rpcdb(srvproc, (int *)NULL);
  551.     rpc_owner = srv_rpcowner(srvproc, (int *)NULL);
  552.     rpc_name = srv_rpcname(srvproc, (int *)NULL);
  553.     
  554.     if (rpc_db != NULL) {
  555.         strcat(complete_rpc_name,rpc_db);
  556.         strcat(complete_rpc_name,".");
  557.     };
  558.     if (rpc_owner != NULL) {
  559.         strcat(complete_rpc_name,rpc_owner);
  560.         strcat(complete_rpc_name,".");
  561.     };
  562.     strcat(complete_rpc_name,rpc_name);
  563.  
  564.     rpc_number = srv_rpcnumber(srvproc);
  565.     if (rpc_number > 0) {
  566.         strcat(complete_rpc_name,";");
  567.         itoa(rpc_number,rpc_number_char,10);
  568.         strcat(complete_rpc_name, rpc_number_char);
  569.     };
  570.     
  571.     dbrpcinit(rmtproc, complete_rpc_name, (DBUSMALLINT)srv_rpcoptions(srvproc));
  572.  
  573.     // Set up any RPC parameters.
  574.     //
  575.     params = srv_rpcparams(srvproc);
  576.     retparam = 1;
  577.  
  578.     for (i = 1; i <= params; i++) {
  579.         rpc_paramname = srv_paramname(srvproc, i, (int *)NULL);
  580.         if (!strlen(rpc_paramname)) 
  581.              rpc_paramname = NULL;
  582.            rpc_paramstatus = (BYTE)srv_paramstatus(srvproc, i);
  583.            rpc_paramtype = srv_paramtype(srvproc, i);
  584.            rpc_parammaxlen = srv_parammaxlen(srvproc, i);
  585.         rpc_paramlen = srv_paramlen(srvproc, i);
  586.            rpc_paramdata = srv_paramdata(srvproc, i);
  587.  
  588.         //  DB-Lib requires maxlen of -1 for fixed-length datatypes, but ODS doesn't
  589.         // always return this.   So set it.
  590.         switch (rpc_paramtype)
  591.         {
  592.             case SQLDECIMAL:
  593.             case SQLNUMERIC:
  594.             case SQLBIT:
  595.             case SQLINT1:
  596.             case SQLINT2:
  597.             case SQLINT4:
  598.             case SQLFLT4:
  599.             case SQLDATETIM4:
  600.             case SQLMONEY4:
  601.             case SQLMONEY:
  602.             case SQLDATETIME:
  603.             case SQLFLT8:
  604.                 rpc_parammaxlen = -1;
  605.                 break ;
  606.             default:
  607.                 break ;
  608.         }
  609.  
  610.         // Special handling for decimal types.  DB-Lib treats them as fixed length datatypes
  611.         // that are the full size of a DBNUMERIC.  ODS uses variable-length numeric data
  612.         // and only gives the exact bytes needed to represent the number 
  613.  
  614.         switch (rpc_paramtype)
  615.         {
  616.             case SQLDECIMAL:
  617.             case SQLNUMERIC:
  618.                 memset (&numeric, 0, sizeof(DBNUMERIC));
  619.                 memmove(&numeric, rpc_paramdata, rpc_paramlen);
  620.                 rpc_paramdata = &numeric;
  621.                 // ODS says null numeric values have a length of 2, dblib expects them to be 0
  622.                 if (rpc_paramlen == 2) 
  623.                     rpc_paramlen = 0;
  624.                 break;
  625.             default:
  626.                 break;
  627.         }
  628.         
  629.         dbrpcparam(rmtproc,rpc_paramname,rpc_paramstatus,rpc_paramtype, 
  630.                      rpc_parammaxlen,rpc_paramlen, rpc_paramdata);
  631.  
  632.         // The current rpc may have three parameters, but only the
  633.         // first and third are return parameters.  This means that
  634.         // dbnumrets() returns two parameters, not three.  The first
  635.         // call to dbretdata() referes to the rpc's first parameter,
  636.         // and the second call to dbretdata() refers to the rpc's
  637.         // third parameter.  To handle this, we map each return
  638.         // parameter to its original parameter so we can later
  639.         // reset the return value of the correct return parameters
  640.         // in "handle_results()".
  641.         //
  642.         if ((BYTE)srv_paramstatus(srvproc, i) & SRV_PARAMRETURN) {
  643.             paramarray[retparam] = i;
  644.             retparam++;
  645.         }
  646.     }
  647.  
  648.     // Send the RPC to the remote DBMS.
  649.     //
  650.     dbrpcsend(rmtproc);
  651.     dbsqlok(rmtproc);
  652.  
  653.     // Now get the remote DBMS results and pass them back to
  654.     // Open Server client.
  655.     //
  656.     handle_results(rmtproc, srvproc);
  657.     return(SRV_CONTINUE);
  658. }
  659.  
  660. //
  661. // HANDLE_RESULTS
  662. //     Once a command has been sent to the remote DBMS by the
  663. //     SRV_LANGUAGE  handler, this routine processes the results
  664. //     and passes them back to the client.
  665. //
  666. // Parameters:
  667. //     rmtproc - The DBPROCESS handle to the remote DBMS.
  668. //     srvproc - The process handle to use to send results to the client.
  669. //
  670. // Returns:
  671. //     SUCCEED or FAIL.
  672. //
  673. int handle_results(rmtproc, srvproc)
  674. DBPROCESS *rmtproc;
  675. SRV_PROC *srvproc;
  676. {
  677.     short cols;             // data columns returned
  678.     int i;                  // index variable
  679.     BOOL results_sent;      // number of result sets sent
  680.     BOOL gotcompute;        // COMPUTE row indicator
  681.     short *paramarray;
  682.     RETCODE returnvalue;    // value returned from dblib call
  683.     DBCOL    dbcol;            // column metadata structure
  684.     TYPEINFO    typeinfo;    // used to send metadata in usertype 
  685.     int dbcolsize;            // size of DBCOL struct
  686.     BOOL bRowsAffected;        // was command one that affects or returns rows
  687.     DBINT nRowsAffected;    // result of DBCOUNT
  688.     DBNUMERIC numeric;        // structure to hold precision and scale for numeric cols    
  689.     short collen = 0;        // holder for col data length
  690.     DBINT    retlen;            // for sending output params
  691.     DBINT    rpc_paramtype;    // for sending output params
  692.  
  693.     results_sent = FALSE;
  694.     gotcompute = FALSE;
  695.     bRowsAffected = FALSE;
  696.     nRowsAffected = 0;
  697.  
  698.     paramarray = ((REMOTE_DBMS *)srv_getuserdata(srvproc))->retparams;
  699.  
  700.     // initialize the DBCOL structure
  701.     dbcolsize = sizeof(DBCOL);    
  702.     memset(&dbcol, 0, dbcolsize);
  703.     dbcol.SizeOfStruct = dbcolsize;
  704.  
  705.     // initialize numeric structure
  706.     memset (&numeric, 0, sizeof(DBNUMERIC));
  707.  
  708.     // Process the results from the remote DBMS.
  709.     // Since a command may consist of multiple commands or a single
  710.     // command that has multiple sets of results, we'll loop through
  711.     // each results set.
  712.     //
  713.     while (TRUE) {
  714.  
  715.         returnvalue = dbresults(rmtproc);
  716.         if (returnvalue == NO_MORE_RESULTS) {
  717.             break;
  718.         }
  719.  
  720.         // Check to see if the client has sent an attention event.  If
  721.         // so, simply discard data from remote server
  722.         //
  723.         if (SRV_GOT_ATTENTION(srvproc)) {
  724.             dbcancel(rmtproc);    // See attn_handler comments
  725.             continue;
  726.         }
  727.  
  728.         //
  729.         // If this is the second time through the loop,
  730.         // send a completion message to the client
  731.         // for the previous results sent.
  732.         //
  733.         if (results_sent == TRUE) {
  734.  
  735.             // If there are some COMPUTE rows, send a message
  736.             // to the client that Data Services Library doesn't yet handle them.
  737.             //
  738.             if (gotcompute == TRUE) {
  739.                 gotcompute = FALSE;
  740.                 srv_sendmsg(srvproc, SRV_MSG_ERROR, (DBINT)COMPUTE_ROW,
  741.                             (DBTINYINT)0, (DBTINYINT)0, NULL, 0, 0,
  742.                           "Data Services library can't handle COMPUTE rows.",
  743.                             SRV_NULLTERM);
  744.             }
  745.  
  746.             // If the previous batch was one that may
  747.             // have affected rows, set the DONE status
  748.             // accordingly.
  749.             //
  750.             if (bRowsAffected) {
  751.                 srv_senddone(srvproc, SRV_DONE_MORE | SRV_DONE_COUNT,
  752.                              (DBUSMALLINT)0, nRowsAffected);
  753.             } else
  754.                 srv_senddone(srvproc, SRV_DONE_MORE, (DBUSMALLINT)0,
  755.                             nRowsAffected);
  756.  
  757.         }
  758.  
  759.         // How many data columns are in the row?
  760.         // Non-"select" statements will have 0 columns.
  761.         //
  762.         cols = dbnumcols(rmtproc);
  763.  
  764.         // Build the row description for the client return.
  765.         //
  766.         for (i = 1; i <= cols; i++) {
  767.  
  768.  
  769.             //  Call "srv_describe()" for each column in the row.
  770.             //  dbcolntype is used to preserve the nullability information
  771.             //    that is lost by dbcoltype.
  772.             //
  773.             srv_describe(srvproc, i, (DBCHAR *)dbcolname(rmtproc, i), SRV_NULLTERM,
  774.                          (DBINT)dbcolntype(rmtproc, i), dbcollen(rmtproc, i),
  775.                          (DBINT)dbcolntype(rmtproc, i), dbcollen(rmtproc, i),
  776.                          (BYTE *)NULL);
  777.  
  778.             // Now pack additional col metadata into the usertype
  779.             // Note this code below does "SQL 6.0" style packing used by DB-Lib, 
  780.             // and the ODS gateway ODBC driver if the resources DLL is set to use it
  781.             // "ODBC-style" metadata encoding uses a different structure, not shown here.
  782.             // See ODS Programmer's Reference for more details.
  783.             // get column metadata
  784.             dbcolinfo(rmtproc, CI_REGULAR, i, 0, &dbcol);
  785.  
  786.             // reset to 0
  787.             *(DBINT*)&typeinfo = 0;
  788.  
  789.             /* Set user type */
  790.             if (dbcol.UserType < 0)
  791.                 typeinfo.typeid = 0;
  792.             else
  793.                 typeinfo.typeid = (USHORT) dbcol.UserType;
  794.  
  795.             if (dbcol.Identity == TRUE)
  796.                    typeinfo.identity = TRUE;
  797.  
  798.             if (dbcol.Null == TRUE)
  799.                    typeinfo.nullable = TRUE;
  800.             else if (dbcol.Null == DBUNKNOWN)
  801.                    typeinfo.nullable = DBUNKNOWN;
  802.  
  803.             if (dbcol.CaseSensitive == TRUE)
  804.                    typeinfo.case_sensitive = TRUE;
  805.             else if (dbcol.CaseSensitive == DBUNKNOWN)
  806.                    typeinfo.case_sensitive = DBUNKNOWN;
  807.  
  808.             if (dbcol.Updatable == TRUE)
  809.                 typeinfo.updateable = TRUE;
  810.             else if (dbcol.Updatable == DBUNKNOWN)
  811.                 typeinfo.updateable = DBUNKNOWN;
  812.             
  813.             srv_setutype(srvproc, i, *(DBINT*)&typeinfo);
  814.  
  815.             // If column is a decimal or numeric, need to setup a valid precision and
  816.             // scale.  Normally this would be provided by dbdata() prior to srv_sendrow().
  817.             // But if there are no rows in the result set, srv_sendrow() will not
  818.             // be called, and ODS will send the header when srv_senddone() is called.
  819.             if ((dbcol.Type == SQLNUMERIC) || (dbcol.Type == SQLDECIMAL))
  820.                 {
  821.                 numeric.precision = dbcol.Precision;
  822.                 numeric.scale = dbcol.Scale;
  823.                 srv_setcoldata(srvproc, i, &numeric);
  824.                 }
  825.         }
  826.  
  827.         // Send each row from the remote DBMS to the client.
  828.         //
  829.         while (TRUE) {
  830.  
  831.             returnvalue = dbnextrow(rmtproc);
  832.             if (returnvalue == NO_MORE_ROWS) {
  833.                 break;
  834.             }
  835.  
  836.             // If it's not a regular row, it's a COMPUTE row.
  837.             // This SQL extension is particular to Sybase
  838.             // TRANSACT-SQL and is not yet supported.
  839.             //
  840.             if (DBROWTYPE(rmtproc) != REG_ROW) {
  841.                 gotcompute = TRUE;
  842.                 continue;
  843.             } else
  844.                 gotcompute = FALSE;
  845.  
  846.             // The row description is built.  Move the
  847.             // rows from the remote server to the client.
  848.             //
  849.             for (i = 1; i <= cols; i++) {
  850.                 srv_setcollen(srvproc, i, (collen = (short)dbdatlen(rmtproc, i)));
  851.                 // special handling for the srv_setcoldata pointer if the value is null numeric
  852.                 // dbdata does not point to a valid numeric structure in this case
  853.                 // so point instead to the numeric structure set up at srv_describe time
  854.                  if (  ((dbcoltype(rmtproc, i) == SQLDECIMAL) || (dbcoltype(rmtproc, i) == SQLNUMERIC))
  855.                      && (collen == 0))
  856.                     {
  857.                     srv_setcoldata(srvproc, i, &numeric);
  858.                     }
  859.                 else
  860.                     srv_setcoldata(srvproc, i, dbdata(rmtproc, i));                
  861.             }
  862.  
  863.             // Check to see if the client has issued an attention event.
  864.             // If so, discard data from the remote server.
  865.             //
  866.             if (SRV_GOT_ATTENTION(srvproc)) {
  867.                 dbcancel(rmtproc);    // See attn_handler comments
  868.                 continue;
  869.             }
  870.  
  871.             // Now put the row in the ODS output buffer
  872.             srv_sendrow(srvproc);
  873.         }
  874.         // Check to see if any parameter were returned from
  875.         // the remote DBMS.  If so, pass them through to the
  876.         // client.
  877.         for (i = 1; i <= dbnumrets(rmtproc); i++) {
  878.             //
  879.             // If the return parameters are a result of
  880.             // an rpc, we used srv_paramset() to set the return
  881.             // value.  If the return parameters are not the
  882.             // result of an rpc, we use srv_returnval().
  883.             //
  884.             retlen = dbretlen(rmtproc, i);
  885.             rpc_paramtype = dbrettype(rmtproc, i);
  886.  
  887.             // special handling for decimal types.  need to add 2 bytes for precision & scale
  888.             // from what is returned by dbretlen
  889.                if ((rpc_paramtype == SQLNUMERIC)    ||     (rpc_paramtype == SQLDECIMAL))
  890.                     retlen += 2;
  891.                                                                 
  892.             if (srv_rpcname(srvproc, NULL) != NULL) {
  893.                 //
  894.                 // The current rpc may have three parameters, but
  895.                 // only the first and third are return parameters.
  896.                 // This means that dbnumrets() returns two parameters,
  897.                 // not three.  The first call to dbretdata() refers to
  898.                 // the rpc's first parameter, and the second call to
  899.                 // dbretdata() refers to the rpc's third parameter.
  900.                 // To handle this, we map each return parameter to
  901.                 // its original parameter so we can later reset the
  902.                 // return value of the correct return parameters in
  903.                 // handle_results().
  904.                 //
  905.                 srv_paramset(srvproc, paramarray[i], dbretdata(rmtproc, i), retlen);
  906.                 
  907.             } else {
  908.                 
  909.                 srv_returnval(srvproc, (DBCHAR *)dbretname(rmtproc, i), SRV_NULLTERM,
  910.                               SRV_PARAMRETURN, rpc_paramtype,
  911.                               retlen, retlen,dbretdata(rmtproc, i));
  912.  
  913.  
  914.             }
  915.         }
  916.         // Check to see if we got a return status code from the
  917.         // remote DBMS.  Pass it through to the client.
  918.         //
  919.         if (dbhasretstat(rmtproc))
  920.            srv_sendstatus(srvproc, dbretstatus(rmtproc));
  921.  
  922.         // If the command was one where count is meaningful
  923.         // send the srv_senddone message accordingly.
  924.         //
  925.         bRowsAffected = dbiscount(rmtproc);        
  926.         nRowsAffected = DBCOUNT(rmtproc);
  927.         
  928.         // Set flag so that we will send a completion
  929.         // message for the current set of results.
  930.         //
  931.         results_sent = TRUE;
  932.     }
  933.  
  934.     // If there are some COMPUTE rows, send a message
  935.     // to the client that Open Services Library doesn't handle them yet.
  936.     //
  937.     if (gotcompute == TRUE) {
  938.         gotcompute = FALSE;
  939.         srv_sendmsg(srvproc, SRV_MSG_ERROR, (DBINT)COMPUTE_ROW, (DBTINYINT)0,
  940.                     (DBTINYINT)0, NULL, 0, 0,
  941.                     "Data Services Library can't handle COMPUTE rows.",
  942.                     SRV_NULLTERM);
  943.     }
  944.  
  945.     // Send the final done packet for the execution of the command batch.
  946.     //
  947.     // If the previous batch was one that may
  948.     // have affected rows, set the DONE status
  949.     // accordingly.
  950.     //
  951.     if (bRowsAffected) {
  952.         srv_senddone(srvproc, SRV_DONE_FINAL | SRV_DONE_COUNT,
  953.                     (DBUSMALLINT)0, nRowsAffected);
  954.        } else
  955.            srv_senddone(srvproc, SRV_DONE_FINAL, (DBUSMALLINT)0,
  956.                     nRowsAffected);
  957.  
  958.     return(SUCCEED);
  959. }
  960.  
  961. // EXIT_REMOTE
  962. //     Handler for SRV_DISCONNECT events.
  963. //     Closes remote DBMS connection if appropriate.
  964. //
  965. //    The code to disconnect from the remote DBMS is SQL Server specific. If
  966. //    using a non-SQL Server database, the disconnect from the  remote database
  967. //    would be different but would probably still occur here.
  968. //
  969. // Parameters:
  970. //     srvproc - the handle to the client connection
  971. //
  972. // Returns:
  973. //     SRV_CONTINUE
  974. //
  975. int exit_remote(srvproc)
  976. SRV_PROC *srvproc;
  977. {
  978.      char *string;
  979.      int len;
  980.     REMOTE_DBMS *remote;    // pointer to target connect structure
  981.  
  982.     remote = (REMOTE_DBMS *)srv_getuserdata(srvproc);
  983.  
  984.     // Is there a REMOTE_DBMS structure to clean-up?
  985.     //
  986.     if (remote != (REMOTE_DBMS *)NULL) {
  987.  
  988.         // Is there a live dbproc?
  989.         //
  990.         if (remote->dbproc != (DBPROCESS *)NULL) {
  991.             dbclose(remote->dbproc);
  992.         }
  993.         dbfreelogin(remote->login);
  994.         srv_free(remote);
  995.     }
  996.  
  997.      // Display info on console
  998.      //
  999.      string = srv_pfield(srvproc, SRV_CPID, &len);
  1000.     string[len] = '\0';
  1001.        printf("\nClient connection closed, process ID: %s\n", string);
  1002.  
  1003.     return(SRV_CONTINUE);
  1004. }
  1005.  
  1006. // CHK_ERR
  1007. //     Print out errors.
  1008. //
  1009. // Parameters:
  1010. //     server        - pointer to gateway server structure.
  1011. //     srvproc      - pointer to client connection structure
  1012. //     errornum     - error number.
  1013. //     severity     - error severity.
  1014. //     state         - error state.
  1015. //     oserrnum     - operating system error number, if any.
  1016. //     errtext      - the text of the error message.
  1017. //     errtextlen   - length of the errtext message
  1018. //     oserrtext    - the text of the operating system error message.
  1019. //     oserrtextlen - length of the errtext message
  1020. //
  1021. // Returns:
  1022. //     SRV_CONTINUE, SRV_CANCEL, or SRV_EXIT
  1023. //
  1024.  
  1025. RETCODE chk_err(server, srvproc, errornum, severity, state, oserrnum, errtext,
  1026.                 errtextlen, oserrtext, oserrtextlen)
  1027. SRV_SERVER *server;
  1028. SRV_PROC *srvproc;
  1029. int errornum;
  1030. BYTE severity;
  1031. BYTE state;
  1032. int oserrnum;
  1033. DBCHAR *errtext;
  1034. int errtextlen;
  1035. DBCHAR *oserrtext;
  1036. int oserrtextlen;
  1037. {
  1038.     char log_buffer[256];
  1039.     char error[256];
  1040.     char oserror[256];
  1041.  
  1042.     memcpy(error, errtext, errtextlen);
  1043.     error[errtextlen] = '\0';
  1044.     memcpy(oserror, oserrtext, oserrtextlen);
  1045.     oserror[oserrtextlen] = '\0';
  1046.  
  1047.     // Strip out resource information. Get the actual error number.
  1048.     errornum = (errornum & 0x0000FFFF);
  1049.  
  1050.     // Operating system error?
  1051.     //
  1052.     if (oserrnum != SRV_ENO_OS_ERR) {
  1053.         sprintf(log_buffer, "SERVER OS ERROR: %d: %s.", oserrnum, oserror);
  1054.  
  1055.         if (server)    
  1056.             srv_log(server, TRUE, log_buffer, SRV_NULLTERM);
  1057.         else    // If application not initialized log to screen
  1058.             printf ("%s\n", log_buffer);
  1059.     }
  1060.  
  1061.     // Is this a fatal error for the server?
  1062.     //
  1063.     if (severity >= SRV_FATAL_SERVER) {
  1064.         sprintf(log_buffer, 
  1065.                       "SERVER: FATAL SERVER ERROR: errornum = %d, severity = %d, state = %d: %s.",  
  1066.                      errornum, severity, state, error);
  1067.         if (server)    
  1068.             srv_log(server, TRUE, log_buffer, SRV_NULLTERM);
  1069.         else    // If application not initialized log to screen
  1070.             printf ("%s\n", log_buffer);
  1071.         return(SRV_EXIT);
  1072.     } else {
  1073.         //
  1074.         // Did the "srvproc" get a fatal error?
  1075.         //
  1076.         if (severity >= SRV_FATAL_PROCESS) {
  1077.             sprintf(log_buffer,
  1078.                           "SERVER: FATAL CONNECT ERROR: errornum = %d, severity = %d, state = %d: %s.",
  1079.                      errornum, severity, state, error);
  1080.             if (server)    
  1081.                 srv_log(server, TRUE, log_buffer, SRV_NULLTERM);
  1082.             else    // If application not initialized log to screen
  1083.                 printf ("%s\n", log_buffer);    
  1084.             return(SRV_CANCEL);
  1085.         }
  1086.     }
  1087.  
  1088.     // A non-fatal error or an information message received.
  1089.     // We'll pass it through to the client.
  1090.     //
  1091.     if (srvproc != (SRV_PROC *)NULL && (server != NULL))
  1092.         if (severity < 10) {    // if informational message
  1093.             srv_sendmsg(srvproc, SRV_MSG_INFO, (DBINT)errornum, severity, 0,
  1094.                         NULL, 0, 0, error, SRV_NULLTERM);
  1095.         } else {            // must be an error message
  1096.         srv_sendmsg(srvproc, SRV_MSG_ERROR, (DBINT)errornum, severity, 0,
  1097.                     NULL, 0, 0, error, SRV_NULLTERM);
  1098.     } else {
  1099.         sprintf(log_buffer, "ODS ERROR: errornum = %d, severity = %d: %s",
  1100.                 errornum, severity, error);
  1101.         if (server)    
  1102.             srv_log(server, TRUE, log_buffer, SRV_NULLTERM);
  1103.         else    // If application not initialized log to screen
  1104.             printf ("%s\n", log_buffer);    
  1105.     }
  1106.     return(SRV_CONTINUE);
  1107. }
  1108.  
  1109. // REMOTEMSGS
  1110. //     DBLIB calls this routine when any messages are received
  1111. //     from the remote DBMS. It gets the remote message information and
  1112. //     sends it back to the client as a message.
  1113. //
  1114. // Parameters:
  1115. //     dbproc    - The handler to the remote DBMS process that sent the message.
  1116. //     msgno     - The message number.
  1117. //     msgstate  - The message state.
  1118. //     severity  - The message severity.
  1119. //     msgtext   - The text of the message.
  1120. //     srvname   - The server name
  1121. //     procname  - The stored procedure name (if there was one)
  1122. //     line      - The line number of the procedure
  1123. //
  1124. //     The following three parameters are available in TDS4.0 and above:
  1125. //
  1126. //     srvname  - The name of the server that sent the message.
  1127. //     procname - The procedure name, if any, of the remote DBMS command.
  1128. //     line  - The remote DBMS command buffer line to which the msg applies.
  1129. //
  1130. // Returns:
  1131. //     0
  1132. //
  1133.  
  1134. int remotemsgs(dbproc, msgno, msgstate, severity, msgtext, srvname, procname, line)
  1135. DBPROCESS *dbproc;
  1136. DBINT msgno;
  1137. INT msgstate;
  1138. INT severity;
  1139. LPCSTR msgtext;
  1140. LPCSTR srvname;
  1141. LPCSTR procname;
  1142. DBUSMALLINT line;
  1143. {
  1144.     SRV_PROC *srvproc;
  1145.  
  1146.     // If a remote DBMS error was received during the remote
  1147.     // open, the dbproc is NULL and a message is sent back on the
  1148.     // most recent srvproc.
  1149.     //
  1150.     if (dbproc == (DBPROCESS *)NULL) {
  1151.         srvproc = Newsrvproc;
  1152.     } else {
  1153.         if ((srvproc = (SRV_PROC *)dbgetuserdata(dbproc)) == NULL) {
  1154.             //
  1155.             // An error was received after the dbproc was assigned, but
  1156.             // before we were able to associate our srvproc.
  1157.             //
  1158.             srvproc = Newsrvproc;
  1159.         }
  1160.     }
  1161.     if (severity < 10) {    // if informational message
  1162.  
  1163.         srv_sendmsg(srvproc, SRV_MSG_INFO, msgno, (DBTINYINT)severity,
  1164.                     (DBTINYINT)msgstate, NULL, 0, 0, (DBCHAR *)msgtext, SRV_NULLTERM);
  1165.         return(0);
  1166.     } 
  1167.  
  1168.     // Trap login fail message
  1169.     if (msgno == REMOTE_FAIL){
  1170.         // Send a message to the client that
  1171.         // the remote connection failed.
  1172.         //
  1173.         srv_sendmsg(srvproc, SRV_MSG_ERROR, (DBINT)msgno, (DBTINYINT)severity,
  1174.                     (DBTINYINT)msgstate, NULL, 0, 0,
  1175.                     "Login to remote DBMS failed (dbopen).", SRV_NULLTERM);
  1176.     }
  1177.  
  1178.     // must be an error message
  1179.     srv_sendmsg(srvproc, SRV_MSG_ERROR, msgno, (DBTINYINT)severity,
  1180.         (DBTINYINT)msgstate, NULL, 0, 0, (DBCHAR *)msgtext, SRV_NULLTERM);
  1181.  
  1182.     return(0);
  1183. }
  1184.  
  1185. // REMOTEERR
  1186. //     This is the handler for error messages from the remote DBMS, in this
  1187. //     case DBLIB.  It gets called whenever a DBLIB error occurs.  It takes
  1188. //     the error message and passes it back to the client.
  1189. //
  1190. // Parameters:
  1191. //     dbproc - The process handle for the remote DBMS.
  1192. //     severity - The severity of the error.
  1193. //     dberr - The DBLIB error number.
  1194. //     oserr - The operating system error, if any.
  1195. //     dberrstr - The text of the DBLIB error.
  1196. //     oserrstr - The text of operating system error, if any.
  1197. //
  1198. // Returns:
  1199. //     INT_EXIT to exit the program.
  1200. //     INT_CANCEL to cause a FAIL return from the DBLIB routine that got
  1201. //     the error.
  1202. //
  1203. int remoteerr(dbproc, severity, dberr, oserr, dberrstr, oserrstr)
  1204. DBPROCESS *dbproc;
  1205. INT severity;
  1206. INT dberr;
  1207. INT oserr;
  1208. LPCSTR dberrstr;
  1209. LPCSTR oserrstr;
  1210. {
  1211.     SRV_PROC *srvproc = (SRV_PROC *)NULL;
  1212.  
  1213.     // If the DBLIB process is dead or we get a DBLIB error SQLESMSG
  1214.     // ("General SQL Server Error:...") then simply ignore it.  The error
  1215.     // message has already been sent to the client.
  1216.     //
  1217.     if (dberr == SQLESMSG) {
  1218.         return(INT_CANCEL);
  1219.     }
  1220.  
  1221.     //
  1222.     // A remote DBMS error may have been issued during the remote
  1223.     // open. In this case, the dbproc will be NULL and a message
  1224.     // will be sent on the most recent srvproc.
  1225.     //
  1226.     if (dbproc == (DBPROCESS *)NULL) {
  1227.         srvproc = Newsrvproc;
  1228.     } else {
  1229.         if ((srvproc = (SRV_PROC *)dbgetuserdata(dbproc)) == NULL) {
  1230.             // An error was issued after the dbproc was assigned but before
  1231.             // we were able to associate our srvproc.
  1232.             //
  1233.             srvproc = Newsrvproc;
  1234.         }
  1235.     }
  1236.  
  1237.     // Trap connection failure error
  1238.     if (dberr == SQLECONN){
  1239.         // Send a message to the client that
  1240.         // the remote connection failed.
  1241.         //
  1242.         srv_sendmsg(srvproc, SRV_MSG_ERROR, (DBINT)REMOTE_FAIL, (DBTINYINT)severity,
  1243.                     (DBTINYINT)0, NULL, 0, 0,
  1244.                     "Unable to establish connection to remote DBMS (dbopen).", SRV_NULLTERM);
  1245.  
  1246.         return(INT_CANCEL);
  1247.     }
  1248.  
  1249.     //
  1250.     // Send error message to client.
  1251.     //
  1252.     srv_sendmsg(srvproc, SRV_MSG_ERROR, (DBINT)REMOTE_MSG,
  1253.                 (DBTINYINT)severity, (DBTINYINT)0, NULL, 0, 0,
  1254.                      (DBCHAR *)dberrstr, SRV_NULLTERM);
  1255.  
  1256.     if (oserr != DBNOERR) {
  1257.         srv_sendmsg(srvproc, SRV_MSG_ERROR, (DBINT)REMOTE_MSG,
  1258.                     (DBTINYINT)severity, (DBTINYINT)0, NULL, 0, 0,
  1259.                           (DBCHAR *)oserrstr, SRV_NULLTERM);
  1260.     }
  1261.     return(INT_CANCEL);
  1262. }
  1263.  
  1264. // ATTN_HANDLER
  1265. //     This is an event handler that will be called when the gateway receives
  1266. //     an attention from a client.
  1267. //
  1268. // Parameters:
  1269. //     srvproc - Pointer to the client connection structure
  1270. //               receiving the attention.
  1271. //
  1272. // Returns:
  1273. //     SRV_CONTINUE
  1274. //
  1275. int attn_handler(srvproc)
  1276. SRV_PROC *srvproc;
  1277. {
  1278.     // Open Data Services NT receives client attention events asynchronously.
  1279.       // Being handle_results may be calling dbresults or dbnextrow, we can not
  1280.     // process the attention with the dbcancel call here. Instead dbcancel
  1281.     // will be called after the attention has been detected using 
  1282.     // SRV_GOT_ATTENTION.
  1283.  
  1284.     return(SRV_CONTINUE);
  1285. }
  1286.  
  1287.  
  1288. #pragma check_stack()   // set stack checking to its default setting
  1289.