/* NAME
 *     sample
 *
 *     Taken from one of Dave Segleau's sample programs.
 *
 * BUILD INSTRUCTIONS
 *     cc -g -I$MI_HOME/h -o sample sample.c $MI_HOME/lib/libmi.a -lm
 *
 */


#include "stdio.h"
#include "mi.h"

/* forward declarations */
void all_callback();
void message_handler();
void exception_handler();
void fatal_handler();
void die();

main(argc, argv)
int     argc;
char    **argv;
{
     MI_CONNECTION  *conn = NULL;
     char           *query = NULL,
                    *Db = "test";
     int             ret;

     /* Set up callback routines.  We can either have one callback which is 
      * called for ANY type of event, or have a different callback for each
      * event type.  We choose to have one for all events, and do the event
      * dispatching ourselves, to illustrate them.
      */

     if (mi_add_callback(MI_All_Events, (MI_VOID) all_callback, NULL)
          == MI_ERROR)
          die("Can't set up callback", conn);

     /* Connect to database using the default user name and password.  */

     if ( (conn = mi_open(Db, NULL, NULL)) == NULL)
          die("connection setup failed", conn);

     /* Create a table. */

     query = "create table example (id integer, name text);";

     /* mi_exec doesn't actually wait for results - it just sends the query 
      * to the server and returns.
      */

     if (mi_exec(conn, query, 0) == MI_ERROR)
          die("can't exec query", conn);

     /* Many queries (such as CREATE TABLE, BEGIN TRANSACTION, etc) return 
      * results which usually aren't particularly interesting.
      * 
      * We are typically only interested in whether those queries failed, 
      * encountered triggers, caused alarms, etc.
      * 
      * For those cases, mi_query_finish() should be used.  It fetches 
      * results until the server is ready for another query, and returns 
      * MI_ERROR if the query failed.  All callbacks are called properly 
      * during processing.
      *
      * Note that even if the query failed, mi_query_finish still guarantees 
      * that the server is ready for another query (unless the server 
      * dropped the connection).
      */

     if (mi_query_finish(conn) == MI_ERROR)
          die("can't create table", conn);

     /* Insert values into our table.  */

     query = "insert into example values (1234, 'Mike Stonebraker');";

     if (mi_exec(conn, query, 0) == MI_ERROR)
          die("can't exec query", conn);
     if (mi_query_finish(conn) == MI_ERROR)
          die("can't do insert", conn);

     query = "insert into example values (2345, 'Jeff Dozier');";

     if (mi_exec(conn, query, 0) == MI_ERROR)
          die("can't exec query", conn);
     if (mi_query_finish(conn) == MI_ERROR)
          die("can't do insert", conn);

     query = "insert into example values (3456, 'Ira Machevsky');";

     if (mi_exec(conn, query, 0) == MI_ERROR)
          die("can't exec query", conn);
     if (mi_query_finish(conn) == MI_ERROR)
          die("can't do insert", conn);

     /* Fetch the values we just inserted into our table.  */

     query = "select * from example;";

     if (mi_exec(conn, query, 0) == MI_ERROR)
          die("can't exec query", conn);

     /* Unlike the above queries, the results from "select" ARE interesting 
      * to us.  We go into a "results loop" to fetch the results.
      * 
      * To illustrate the use of the library, we do this with more generality 
      * than the typical application may require.
      */

     while ((ret = mi_get_result(conn)) != MI_NO_MORE_RESULTS) {
          switch (ret) {
               case MI_ERROR:
                    die("mi_get_result() failed.", conn);
                    break;
               case MI_DDL:                /* FALLTHROUGH */
               case MI_DML:
                    break;
               case MI_ROWS:               /* Print data */
                    if(print_rows(conn) == MI_ERROR)
                         die("print_rows failed", conn);
                    break;
               default:
                    die("Unexpected result from mi_get_result()", conn);
                    break;
          }
     }

     /* Now that we have successfully examined our table, we can drop it. 
      * Like many of the above queries, we are only interested in the 
      * successful completion of our query.
      */

     query = "drop table example;";
     if (mi_exec(conn, query, 0) == MI_ERROR)
          die("can't exec query", conn);
     if (mi_query_finish(conn) == MI_ERROR)
          die("drop table failed", conn);

     /* Now, we're done!  We shut down our connection and exit.
      * 
      * If we exit without shutting down our connection, nothing bad will 
      * happen, but it is good practice, especially if this routine were to 
      * become a regular function rather than a "main".
      */

     if (mi_close(conn) == MI_ERROR)
          die("can't close connection", NULL);
     else
          exit(0);
}

int
print_rows(conn)
MI_CONNECTION *conn;
{
     MI_ROW       *row;
     MI_ROW_DESC  *desc;
     int          error = 0, ncols, i;
     int          entering = 1;

     while ((row = mi_next_row(conn, &error)) != NULL) {
          /*
           * Note that the row descriptor CAN change in
           * mid-query for some types of queries,
           * primarily those involving sets.
           */

          if (entering) {
               desc = mi_get_row_desc(row);
               ncols = mi_column_count(desc);
               for (i = 0; i < ncols; i++) {
                    printf("%s\t", mi_column_name(desc, i));
               }
               entering = 0;
               printf("\n\n");
          }
          for (i = 0; i < ncols; i++) {
               char *col_val;
               int  col_len;
               switch (mi_value(row, i, &col_val, &col_len)) {
                  case MI_ERROR:
                     fprintf(stderr, "mi_value failed");
                     return(MI_ERROR);
                  case MI_NULL_VALUE:
                     col_val = "NULL";
                     break;
                  case MI_NORMAL_VALUE:
                     break;
                  default:
                     fprintf(stderr, "Unexpected column type found.");
                     return(MI_ERROR);
               }

               /* column value is null-terminated for ASCII returns */
               printf("%s\t", col_val);
          }
          printf("\n");

     } /* End process each row */

     if (error == MI_ERROR) {
          fprintf(stderr, "mi_next_row failed");
          return(MI_ERROR);
     }

     return(0);
}


/*
 * Error and callback declarations.
 */

/*
 * This routine dispatches callback events.  It takes
 * the connection type and figures out which routine
 * to call.
 */

void
all_callback(type, conn, s1, s2)
MI_EVENT_TYPE   type;
MI_CONNECTION  *conn;
void           *s1, *s2;
{
     int       ecode;
     char      emsg[100];
     char      *s;

     switch (type)
     {
          /* Some sort of Server error. */
          case MI_Exception:
               ecode = mi_error_level(s1);
               switch (mi_error_level(s1))
               {
                    case MI_MESSAGE:
                         message_handler(s1, s2);
                         break;
                    case MI_EXCEPTION:
                         exception_handler(s1, s2);
                         break;
                    case MI_FATAL:
                         fatal_handler(s1, s2);
                         break;
               }
               break;

          /* A Client Library Error is any type of internal library error,
           * library usage problem, or dropped connection.
           */

          case MI_Client_Library_Error:
               ecode = mi_error_level(s1);
               switch (ecode)
               {
                    case MI_LIB_BADARG:
                         s = "MI_LIB_BADARG";
                         break;
                    case MI_LIB_USAGE:
                         s = "MI_LIB_USAGE";
                         break;
                    case MI_LIB_INTERR:
                         s = "MI_LIB_INTERR";
                         break;
                    case MI_LIB_NOIMP:
                         s = "MI_LIB_NOIMP";
                         break;
                    case MI_LIB_DROPCONN:
                         s = "MI_LIB_DROPCONN";
                         break;
                    default:
                         s = "UNKNOWN";
                         break;
               }
               mi_errmsg(s1, emsg, 100);
               fprintf(stderr, "%s: %s\n", s, emsg);
               break;

          /* An Alerter event occured.  */
          case MI_Alerter_Fire_Msg:
               fprintf(stderr, "Alerter %s %s\n",
                    mi_alert_name(s1),
                    mi_alert_status(s1) == MI_ALERTER_DROPPED ?
                    "dropped" : "fired");
               break;

         /* We aren't expecting any other events here, although they could 
          * happen.  Just let the user know it happened and continue.
          */
          default:

               fprintf(stderr, "Caught an unexpected event type\n");
               break;
     } /* End switch event type */
}

/* Used for MI_MESSAGE's.  */

void
message_handler(s1, s2)
void *s1, *s2;
{
     char   buf[8192];
     mi_errmsg(s1, buf, 8192);
     fprintf(stderr, "%s\n", buf);
     return;
}

/* used for MI_EXCEPTION's.  */

void
exception_handler(s1, s2)
void *s1, *s2;

{
     char  buf[8192];
     mi_errmsg(s1, buf, 8192);
     fprintf(stderr, "%s\n", buf);
     return;
}

/*
 * Used for FATAL's.
 */

void
fatal_handler(s1, s2)
void  *s1, *s2;

{
     char buf[8192];
     mi_errmsg(s1, buf, 8192);
     fprintf(stderr, "%s\n", buf);
     return; 
}


void
die(msg, conn)
char           *msg;
MI_CONNECTION  *conn;
{
     fprintf(stderr, "%s\n", msg);

     /* If we have a valid connection, close it.  */

     if (conn != NULL)
          mi_close(conn);
     exit(1);
}


Last modified: 6-June-94
Illustra Rev:  2.0.12
Jean Anderson (jta@postgres.berkeley.edu)