The following example 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 online example programs. See the Client-Library chapter in the Open Client/Server Programmer’s Supplement for information on making and running the online example programs.
/*
** Language Query Example Program.
*/
#include <stdio.h>
#include <ctpublic.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 csmsg_callback();
CS_RETCODE clientmsg_callback();
CS_RETCODE servermsg_callback();
/*
** Main entry point for the program.
*/
int main(argc, argv)
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];
/*
** 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(CS_VERSION_100, &context);
EXIT_ON_FAIL(context, ret, "cs_ctx_alloc failed");
/*
** Initialize Client-Library.
*/
ret = ct_init(context, CS_VERSION_100);
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 inforamational
** 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,
"pooh", CS_NULLTERM, NULL);
EXIT_ON_FAIL(context, ret, "Could not set user name");
ret = ct_con_props(connection, CS_SET, CS_PASSWORD,
"tigger2", CS_NULLTERM, NULL);
EXIT_ON_FAIL(context, ret, "Could not set password");
/*
** Create the connection.
*/
ret = ct_connect(connection, (CS_CHAR *) NULL, 0);
EXIT_ON_FAIL(context, ret, "Could not connect!");
/*
** Step 4: Send a command to the server, as follows:
** - Allocate a CS_COMMAND structure
** - Build the 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 servermsg_callback(cp, chp, msgp)
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 clientmsg_callback(context, conn, emsgp)
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 csmsg_callback(context, emsgp)
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);
}