/* * SantaBarbara.c -- listen for queries on Santa Barbara County * * USAGE * SantaBarbara * * BUILD Instructions: * * Solaris: cc -I$MI_HOME/h -g -o SantaBarbara SantaBarbara.c \ * -lsocket -lnsl $MI_HOME/lib/libmiro.a * * SunOS: cc -I$MI_HOME/h -g -o SantaBarbara SantaBarbara.c \ * $MI_HOME/lib/libmiro.a * * Alpha: * * HISTORY * Pillaged from MI_HOME/examples/real_estate/bigsale.c */ #include #include MI_CONNECTION *do_connect(); void alert_callback(); void die(); void error_callback(); void notify_user(); int ALERTER_FLAG; main(argc, argv) int argc; char **argv; { MI_CONNECTION *conn = (MI_CONNECTION *) NULL; char query[BUFSIZ]; char *db; int ret; /* get database name from command line */ if (argc < 2) die("missing argument \"database\"", conn); db = *++argv; /* * 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_Alerter_Fire_Msg, (MI_VOID) alert_callback, NULL) == MI_ERROR) { die("Can't set up callback", conn); } if (mi_add_callback(MI_Exception, (MI_VOID) error_callback, NULL) == MI_ERROR) { die("Can't set up callback", conn); } if (mi_add_callback(MI_Client_Library_Error, (MI_VOID) error_callback, NULL) == MI_ERROR) { die("Can't set up callback", conn); } /* connect to server */ if ((conn = mi_open(db, NULL, NULL)) == (MI_CONNECTION *) NULL) die("connection setup failed", conn); /* listen for the alerter */ (void) strcpy(query, "listen SantaB with (mechanism = 'callback');"); 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); /* basically, do nothing except poll for an alert (hoping it comes in) */ (void) strcpy(query, "poll SantaB;"); for (;;) { ALERTER_FLAG = 0; if (mi_exec(conn, query, 0) == MI_ERROR) die("can't exec query", conn); if (mi_query_finish(conn) == MI_ERROR) die("can't poll alerter", conn); if (ALERTER_FLAG) notify_user(conn); (void) sleep(3); } /* NOTREACHED */ } void die(msg, conn) char *msg; MI_CONNECTION *conn; { fprintf(stderr, "SantaBarbara: %s\n", msg); /* * If we have a valid connection, close it. */ if (conn != NULL) mi_close(conn); exit(1); } /* * Handle alert callbacks. */ /* ARGSUSED */ void alert_callback(type, conn, s1, s2) MI_EVENT_TYPE type; MI_CONNECTION *conn; void *s1; void *s2; { ALERTER_FLAG = 1; (void) printf("\n\nAlerter %s %s. Here is all activity for the last 5 minutes: \n", mi_alert_name(s1), mi_alert_status(s1) == MI_ALERTER_DROPPED ? "dropped!" : "fired"); /* we only care about high_price */ if (strcmp(mi_alert_name(s1), "high_price")) return; if (mi_alert_status(s1) == MI_ALERTER_DROPPED) die("no more alerter!", conn); } void notify_user(conn) MI_CONNECTION *conn; { char *query; MI_ROW *row; MI_ROW_DESC *desc; int error, ret, ncols, i, entering; /* Output all activity for the last 5 minutes */ query = "select s2k_user, s2k_ops, s2k_info, s2k_date from s2k_audit where s2k_obj='points' and ('now'::abstime - s2k_date::abstime) <= '@ 300 seconds' order by s2k_date desc;"; if (mi_exec(conn, query, 0) == MI_ERROR) die("can't exec select query", conn); /* enter results loop for select query */ 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: 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); printf("\n"); for (i = 0; i < ncols; i++) printf("%15s ", mi_column_name(desc, i)); printf("\n"); entering = 0; } for (i = 0; i < ncols; i++) { char *col_val, *p; 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 */ if(i == 0) printf("\n"); if(i == 1) switch (col_val[0]) { case 's': p = "select"; break; case 'u': p = "update"; break; case 'd': p = "delete"; break; case 'i': p = "insert"; break; default: break; } else p = col_val; printf("%15s ", p); } } if (error) die("mi_next_row failed", conn); break; default: die("Unexpected result from mi_get_results()", conn); break; } } printf("\n"); } /* * Used for notices. */ void notice_handler(s1, s2) void *s1, *s2; { char buf[8192]; mi_errmsg(s1, buf, 8192); fprintf(stderr, "%s\n", buf); } /* * used for WARN's. */ void warn_handler(s1, s2) void *s1, *s2; { char buf[8192]; mi_errmsg(s1, buf, 8192); fprintf(stderr, "%s\n", buf); } /* * 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); } /* * This routine dispatches callback events. It takes the connection type * and figures out which routine to call. */ void error_callback(type, conn, s1, s2) MI_EVENT_TYPE type; MI_CONNECTION *conn; void *s1; void *s2; { int ecode; char emsg[100]; char *s; switch(type) { /* * In the BETA version of the library, an "exception" is any type * of server message, including SQL Warnings or Exceptions. The * constants below are left-overs from the Postgres days and will * change soon, but the control logic will not change. */ case MI_Exception: ecode = mi_error_code(s1); switch(mi_error_code(s1)) { case MI_NOTICE: notice_handler(s1, s2); break; case MI_WARN: warn_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_code(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 alerters or 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; } }