Source listing

The following sample program, called firstapp.c, demonstrates the steps outlined in the previous section. Commentary for each step follows the example (beginning with “Step 1: Set up the Client-Library programming environment”).

The source code for this application is included with the Client-Library sample programs. See the Client-Library chapter in the Open Client and Open Server Programmer’s Supplement for Microsoft Windows or Open Client and Open Server Programmer’s Supplement for UNIX for information on making and running the sample programs.

 /*
 ** Language Query Example Program.
 */

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctpublic.h>
 #include "example.h"

 #define MAXCOLUMNS   2
 #define MAXSTRING    40
 #define ERR_CH stderr
 #define OUT_CH stdout

 /*
 ** Define a macro that exits if a function return code indicates
 ** failure.
 */
 #define EXIT_ON_FAIL(context, ret, str) \
   if (ret != CS_SUCCEED) \
   { \
     fprintf(ERR_CH, "Fatal error: %s\n", str); \
     if (context != (CS_CONTEXT *) NULL) \
     { \
       (CS_VOID) ct_exit(context, CS_FORCE_EXIT); \
       (CS_VOID) cs_ctx_drop(context); \
     } \
     exit(-1); \
   } 

 /*
 ** Callback routines for library errors and server messages.
 */
 CS_RETCODE CS_PUBLIC csmsg_callback PROTOTYPE((
         CS_CONTEXT     *context,
         CS_CLIENTMSG   *clientmsg ));
 CS_RETCODE CS_PUBLIC clientmsg_callback PROTOTYPE((
         CS_CONTEXT     *context,
     CS_CONNECTION  *connection,
         CS_CLIENTMSG   *clientmsg ));
 CS_RETCODE CS_PUBLIC servermsg_callback PROTOTYPE((
         CS_CONTEXT     *context,
     CS_CONNECTION  *connection,
         CS_CLIENTMSG   *servermsg ));

 /*
 ** Main entry point for the program.
 */
 int
 main(int argc, char *argv[])
 {
     CS_CONTEXT     *context;     /* Context structure     */
     CS_CONNECTION  *connection;  /* Connection structure. */
     CS_COMMAND     *cmd;         /* Command structure.    */
 
     /* Data format structures for column descriptions: */
     CS_DATAFMT     columns[MAXCOLUMNS];    
 
     CS_INT         datalength[MAXCOLUMNS];
     CS_SMALLINT    indicator[MAXCOLUMNS];
     CS_INT         count;
     CS_RETCODE     ret;
     CS_RETCODE     results_ret;
     CS_INT         result_type;
     CS_CHAR        name[MAXSTRING];
     CS_CHAR        city[MAXSTRING];
 
     EX_SCREEN_INIT();
 
     /*
     ** Step 1: Initialize the application.
     */

For more commentary, see “Step 1: Set up the Client-Library programming environment”.

     /*
     ** First allocate a context structure.
     */
     context = (CS_CONTEXT *) NULL;
     ret = cs_ctx_alloc(EX_CTLIB_VERSION, &context);
     EXIT_ON_FAIL(context, ret, "cs_ctx_alloc failed");
 
     /*
     ** Initialize Client-Library.
     */
     ret = ct_init(context, EX_CTLIB_VERSION);
     EXIT_ON_FAIL(context, ret, "ct_init failed");
    /*
     ** Step 2: Set up the error handling. Install callback handlers
     ** for: - CS-Library errors - Client-Library errors - Server
     ** messages
     */

For more commentary, see “Step 2: Define error handling”.

     /*
     ** Install a callback function to handle CS-Library errors
     */
     ret = cs_config(context, CS_SET, CS_MESSAGE_CB, 
                     (CS_VOID *)csmsg_callback,
                     CS_UNUSED, NULL);
     EXIT_ON_FAIL(context, ret, 
                 "cs_config(CS_MESSAGE_CB) failed");
 
     /*
     ** Install a callback function to handle Client-Library errors
     ** 
     ** The client message callback receives error or informational
     ** messages discovered by Client-Library.
     */
     ret = ct_callback(context, NULL, CS_SET, CS_CLIENTMSG_CB,
                       (CS_VOID *) clientmsg_callback);
     EXIT_ON_FAIL(context,ret,
                 "ct_callback for client messages failed");
 
     /*
     ** The server message callback receives server messages sent by
     ** the server. These are error or informational messages.
     */
     ret = ct_callback(context, NULL, CS_SET, CS_SERVERMSG_CB,
                       (CS_VOID *) servermsg_callback);
     EXIT_ON_FAIL(context, ret, 
                 "ct_callback for server messages failed");

     /*
     ** Step 3: Connect to the server. We must: - Allocate a connection
     ** structure. - Set user name and password. - Create the
     ** connection.
     */

For more commentary, see “Step 3: Connect to a server”.

    /*
     ** First, allocate a connection structure.
     */
     ret = ct_con_alloc(context, &connection);
     EXIT_ON_FAIL(context, ret, "ct_con_alloc() failed");
     /*
     ** These two calls set the user credentials (username and
     ** password) for opening the connection.
     */
     ret = ct_con_props(connection, CS_SET, CS_USERNAME, 
                Ex_username, CS_NULLTERM, NULL);
     EXIT_ON_FAIL(context, ret, "Could not set user name");
     ret = ct_con_props(connection, CS_SET, CS_PASSWORD, 
                Ex_password, CS_NULLTERM, NULL);
     EXIT_ON_FAIL(context, ret, "Could not set password");
 
     /*
     ** Create the connection.
     */
     if(EX_SERVER==NULL)
         ret = ct_connect(connection, (CS_CHAR *) NULL, 0);
     else
         ret = ct_connect(connection, (CS_CHAR *)EX_SERVER, strlen(EX_SERVER));
     EXIT_ON_FAIL(context, ret, "Could not connect!");
 
     /*
     ** Step 4:  Send a command to the server, as follows: - Allocate a
     ** CS_COMMAND structure - Build a command to be sent with
     ** ct_command. - Send the command with ct_send.
     */

For more commentary, see “Step 4: Send commands to the server”.

     /*
     ** Allocate a command structure.
     */
     ret = ct_cmd_alloc(connection, &cmd);
     EXIT_ON_FAIL(context, ret, "ct_cmd_alloc() failed");
 
     /*
     ** Initiate a language command. This call associates a query with
     ** the command structure.
     */
     ret = ct_command(cmd, CS_LANG_CMD,
              "select au_lname, city from pubs2..authors \
              where state = 'CA'",
              CS_NULLTERM, CS_UNUSED); 
     EXIT_ON_FAIL(context, ret, "ct_command() failed");
 
     /*
     ** Send the command. 
     */
     ret = ct_send(cmd);
     EXIT_ON_FAIL(context, ret, "ct_send() failed");
     /*
     ** Step 5: Process the results of the command.
     */

For more commentary, see “Step 5: Process the results of the command”.

     while ((results_ret = ct_results(cmd, &result_type)) 
            == CS_SUCCEED)
     {
 
         /*
         ** ct_results sets result_type to indicate when data is 
         ** available and to indicate command status codes.
         */
         switch ((int)result_type)
         {
         case CS_ROW_RESULT:
            /* 
             ** This result_type value indicates that the rows
             ** returned by the query have arrived. We bind and
             ** fetch the rows.
             **
             ** We're expecting exactly two character columns:
             ** Column 1 is au_lname, 2 is au_city.
             **
             ** For each column, fill in the relevant fields in
             ** the column's data format structure, and bind
             ** the column.
             */
             columns[0].datatype = CS_CHAR_TYPE;
             columns[0].format = CS_FMT_NULLTERM;
             columns[0].maxlength = MAXSTRING;
             columns[0].count = 1;
             columns[0].locale = NULL;
             ret = ct_bind(cmd, 1, &columns[0], 
                          name, &datalength[0],
                          &indicator[0]);
             EXIT_ON_FAIL(context, ret, 
               "ct_bind() for au_lname failed");
 
             /* 
             ** Same thing for the 'city' column.
             */
             columns[1].datatype = CS_CHAR_TYPE;
             columns[1].format = CS_FMT_NULLTERM;
             columns[1].maxlength = MAXSTRING;
             columns[1].count = 1;
             columns[1].locale = NULL;
             ret = ct_bind(cmd, 2, &columns[1], city,
                          &datalength[1],
                          &indicator[1]);
             EXIT_ON_FAIL(context, ret, 
               "ct_bind() for city failed"); 
             /*
             ** Now fetch and print the rows.

             */
             while(((ret = ct_fetch(cmd, CS_UNUSED, CS_UNUSED,
                                   CS_UNUSED, &count))
                     == CS_SUCCEED) 
                   || (ret == CS_ROW_FAIL))
             {
                 /*
                 ** Check if we hit a recoverable error.
                 */
                 if( ret == CS_ROW_FAIL )
                 {
                     fprintf(ERR_CH,
                             "Error on row %ld.\n",
                             (long)(count+1));
                 }
                 /*
                 ** We have a row, let's print it.
                 */
                 fprintf(OUT_CH, "%s: %s\n", name, city);
             }
 
             /*
             ** We're finished processing rows, so check 
             ** ct_fetch's final return value to see if an 
             ** error occurred. The final return code should be
             ** CS_END_DATA.
             */
             if ( ret == CS_END_DATA )
             {
                 fprintf(OUT_CH,
                         "\nAll done processing rows.\n");
             }
             else /* Failure occurred. */
             {
                 EXIT_ON_FAIL(context, CS_FAIL, 
                             "ct_fetch failed");
             }
 
             /*
             ** All done with this result set.
             */
             break;
       case CS_CMD_SUCCEED:
 
             /*
             ** We executed a command that never returns rows.
             */
             fprintf(OUT_CH, "No rows returned.\n");
             break;
 
         case CS_CMD_FAIL:
 
             /*
             ** The server encountered an error while
             ** processing our command. These errors will be
             ** displayed by the server-message callback that
             ** we installed earlier.
             */
             break;
 
         case CS_CMD_DONE:
 
             /*
             ** The logical command has been completely
             ** processed.
             */
             break;
 
         default:
 
             /*
             ** We got something unexpected.
             */
             EXIT_ON_FAIL(context, CS_FAIL, 
                "ct_results returned unexpected result type");
             break;
         }
     }
     /*
     ** We've finished processing results. Check the return value
     ** of ct_results() to see if everything went okay.
     */
     switch( (int) results_ret)
     {
     case CS_END_RESULTS:
 
             /*
             ** Everything went fine.
             */
             break;
 
         case CS_FAIL:
 
             /*
             ** Something terrible happened.
             */
             EXIT_ON_FAIL(context, CS_FAIL, 
                         "ct_results() returned CS_FAIL.");
             break;
 
         default:
 
             /*
             ** We got an unexpected return value.
             */
             EXIT_ON_FAIL(context, CS_FAIL,
                "ct_results returned unexpected return code");    
             break;
     }
 
     /*
     ** Step 6:  Clean up and exit.
     */

For more commentary, see “Step 6: Finish”.

     /* 
     ** Drop the command structure.
     */
     ret = ct_cmd_drop(cmd);
     EXIT_ON_FAIL(context, ret, "ct_cmd_drop failed");
     /*
     ** Close the connection and drop its control structure.
     */
     ret = ct_close(connection, CS_UNUSED); 
     EXIT_ON_FAIL(context, ret, "ct_close failed");
     ret = ct_con_drop(connection);
     EXIT_ON_FAIL(context, ret, "ct_con_drop failed");
     /*
     ** ct_exit tells Client-Library that we are done. 
     */
     ret = ct_exit(context, CS_UNUSED);
     EXIT_ON_FAIL(context, ret, "ct_exit failed");
     /*
     ** Drop the context structure.
     */
     ret = cs_ctx_drop(context);
     EXIT_ON_FAIL(context, ret, "cs_ctx_drop failed");
     /*
     ** Normal exit to the operating system.
     */
     exit(0);
 }
 
 /*
 ** Handler for server messages. Client-Library will call this
 ** routine when it receives a message from the server.
 */
 CS_RETCODE CS_PUBLIC
 servermsg_callback(CS_CONTEXT *cp, CS_CONNECTION *chp, CS_SERVERMSG *msgp)
 {
 
     /* 
     ** Print the message info. 
     */
     fprintf(ERR_CH, 
         "Server message:\n\t");
     fprintf(ERR_CH, 
         "number(%ld) severity(%ld) state(%ld) line(%ld)\n",
         (long) msgp->msgnumber, (long) msgp->severity, 
         (long) msgp->state, (long) msgp->line);
     /*
     ** Print the server name if one was supplied.
     */
     if (msgp->svrnlen > 0)
         fprintf(ERR_CH, "\tServer name: %s\n", msgp->svrname);
     /* 
     ** Print the procedure name if one was supplied.
     */
     if (msgp->proclen > 0)
         fprintf(ERR_CH, "\tProcedure name: %s\n", msgp->proc);
     /* 
     ** Print the null terminated message. 
     */
     fprintf(ERR_CH, "\t%s\n", msgp->text);
     /*
     ** Server message callbacks must return CS_SUCCEED.
     */
     return(CS_SUCCEED);
 }
 /*
 **  Client-Library error handler. This function will be invoked
 **  when a Client-Library has detected an error. Before Client-
 **  Library routines return CS_FAIL, this handler will be called
 **  with additional error information.
 */
 CS_RETCODE CS_PUBLIC
 clientmsg_callback(CS_CONTEXT *context, CS_CONNECTION *conn, CS_CLIENTMSG 
 *emsgp)
 {
 
      /* 
      ** Error number:
      ** Print the error's severity, number, origin, and 
      ** layer. These four numbers uniquely identify the error.
      */
      fprintf(ERR_CH, 
              "Client Library error:\n\t");
      fprintf(ERR_CH,
         "severity(%ld) number(%ld) origin(%ld) layer(%ld)\n",
         (long) CS_SEVERITY(emsgp->severity), 
         (long) CS_NUMBER(emsgp->msgnumber),
         (long) CS_ORIGIN(emsgp->msgnumber),
         (long) CS_LAYER(emsgp->msgnumber));
      /*
      ** Error text:
      ** Print the error text.
      */
      fprintf(ERR_CH, "\t%s\n", emsgp->msgstring);
 
      /*
      ** Operating system error information: Some errors, such as
      ** network errors, may have an operating system error associated
      ** with them. If there was an operating system error, this code
      ** prints the error message text.
      */
      if (emsgp->osstringlen > 0)
      {
           fprintf(ERR_CH, 
                   "Operating system error number(%ld):\n", 
                   (long) emsgp->osnumber);
           fprintf(ERR_CH, "\t%s\n", emsgp->osstring);
      }
 
      /* 
      ** If we return CS_FAIL, Client-Library marks the connection as
      ** dead. This means that it cannot be used anymore. If we return
      ** CS_SUCCEED, the connection remains alive if it was not already
      ** dead.
      */
      return (CS_SUCCEED);
 }
 
 /*
 **  CS-Library error handler. This function will be invoked
 **  when CS-Library has detected an error. 
 */
 CS_RETCODE CS_PUBLIC
 csmsg_callback(CS_CONTEXT *context, CS_CLIENTMSG *emsgp)
 {
  /*
   ** Print the error number and message.
   */
   fprintf(ERR_CH, 
           "CS-Library error:\n");
   fprintf(ERR_CH, 
            "\tseverity(%ld) layer(%ld) origin(%ld) number(%ld)",
            (long) CS_SEVERITY(emsgp->msgnumber), 
            (long) CS_LAYER(emsgp->msgnumber), 
            (long) CS_ORIGIN(emsgp->msgnumber), 
            (long) CS_NUMBER(emsgp->msgnumber));
   fprintf(ERR_CH, "\t%s\n", emsgp->msgstring);

   /*
   ** Print any operating system error information.
   */
   if(emsgp->osstringlen > 0)
   {
     fprintf(ERR_CH, "Operating System Error: %s\n",
       emsgp->osstring);
   }
   return (CS_SUCCEED);
 }