/* 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.12Jean Anderson (jta@postgres.berkeley.edu)