/* * NAME * example - Dave Segleau's example libmi program * * USAGE * example [-d database] * * ARGUMENTS * -d database name * * DESCRIPTION * - creates table "example" * - sets up alerter "done_inserting" * - inserts into "example" * - fires the alerter * - selects alert results * - drops "example" and the alerter * * MODS * 4/11/94 jta works with Montage 2.0.9, libmontage '3.0.1 M' * 5/31/94 jta works with Illustra 2.0.12 * 7/22/94 jta works with 2.1 * * $Header: /usr/local/devel/montage/samples/programs/RCS/example.c,v 1.4 1994/07/22 23:03:10 jta Exp $ */ /* * All the libmi declarations, structures, etc are * declared in mi.h */ #include #include "mi_services.h" void self_alert_setup(); main(argc, argv) int argc; char **argv; { MI_CONNECTION *conn = NULL; MI_ROW *row; MI_ROW_DESC *desc; int error, ret, ncols, i; char *query, *Db = NULL, library_ver[50], *server_query="return release();"; int entering = 1; int c, errflg = 0; extern char *optarg; extern int optind; /* ========== Get command line arguments. ========== */ while ((c = getopt(argc, argv, "d:")) != -1) { switch (c) { case 'd': Db = optarg; break; case '?': errflg++; break; } } if (errflg || optind < argc) { (void) fprintf(stderr, "usage: %s [-d db]\n", argv[0]); exit(2); } /* * Here, we set up our 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. For * further discussion of the callbacks used here, * see below. */ if (mi_add_callback(MI_All_Events, (MI_VOID) all_callback, NULL) == MI_ERROR) { die("Can't set up callback", conn); } /* * Connect to the Illustra server, * using the default user name and password. */ if ((conn = mi_open(Db, NULL, NULL)) == NULL) die("connection setup failed", conn); if( mi_library_version (library_ver, sizeof(library_ver) - 1) == MI_ERROR) die("dyn_query: mi_library_version failed", conn); printf("libmi version = '%s'\n", library_ver); printf("server version= "); if (exec_query(conn, server_query, 0) == MI_ERROR) die("select failed.", conn); /* * Now, we try to create a table. */ query = "create table example (id integer, name text);"; /* * Note that 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 will fetch 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); /* * Just for kicks, lets setup an Alerter which we * will fire as we transition from inserting to * fetching from our table. Call a local function * to do the actual work. */ self_alert_setup(conn, "done_inserting"); /* * Now, we insert three values into our table. */ query = "insert into example values (1234, 'Jeff Meredith');"; 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, 'Wei Hong');"; 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, 'Jim Shankland');"; 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); /* * Now, let us fire that alerter we setup previously. */ query = "alert done_inserting;"; if (mi_exec(conn, query, 0) == MI_ERROR) die("can't exec query", conn); if (mi_query_finish(conn) == MI_ERROR) die("can't fire alerter", conn); /* * Now, we fetch the values we just inserted into our * table. */ query = "select * from alerters;"; 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: printf("query command was %s ", mi_result_command_name(conn)); printf("number of rows affected was %d\n", mi_result_row_count(conn)); break; case MI_ROWS: 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("column name is %s", mi_column_name(desc, i)); printf(" column number is %d\n", i); } entering = 0; } for (i = 0; i < ncols; i++) { char *col_val; int col_len; switch (mi_value(row, i, &col_val, &col_len)) { case MI_ERROR: die("mi_value failed", conn); break; case MI_NULL_VALUE: col_val = "NULL"; break; case MI_NORMAL_VALUE: break; default: die("Unexpected column type found", conn); break; } /* * column value is null-terminated for * ASCII returns */ printf("%s ", col_val); } printf("\n"); } if (error) die("mi_next_row failed", conn); break; default: die("Unexpected result from mi_get_results()", 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); query = "drop alerter done_inserting;"; 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); } /* * setup an Alerter called , and listen * on it too. */ void self_alert_setup(conn, alerter_name) MI_CONNECTION *conn; char *alerter_name; { char query[80]; /* * Create an alerter. We specify we want the * asyncronous "callback" style alerter mechanism. */ sprintf(query, "create alerter %s (mechanism = 'callback');", alerter_name); if (mi_exec(conn, query, 0) == MI_ERROR) die("can't exec query", conn); if (mi_query_finish(conn) == MI_ERROR) die("can't create alerter", conn); /* * Now, mention that we're interested in this Alerter * ourselves, and don't bother overriding the * alerting mechanism the alerter was created with. */ sprintf(query, "listen %s;", alerter_name); if (mi_exec(conn, query, 0) == MI_ERROR) die("can't exec query", conn); if (mi_query_finish(conn) == MI_ERROR) die("can't listen on alerter", conn); return; }