diff options
Diffstat (limited to 'src/interfaces/libpgtcl/pgtclCmds.c')
-rw-r--r-- | src/interfaces/libpgtcl/pgtclCmds.c | 238 |
1 files changed, 116 insertions, 122 deletions
diff --git a/src/interfaces/libpgtcl/pgtclCmds.c b/src/interfaces/libpgtcl/pgtclCmds.c index b7eae9d6b78..5b3d5e91d54 100644 --- a/src/interfaces/libpgtcl/pgtclCmds.c +++ b/src/interfaces/libpgtcl/pgtclCmds.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.24 1998/06/15 19:30:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.25 1998/06/16 04:10:16 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -15,6 +15,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <ctype.h> #include <tcl.h> #include "postgres.h" @@ -415,7 +416,6 @@ Pg_exec(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) conn = PgGetConnectionId(interp, argv[1], &connid); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR; } @@ -426,6 +426,10 @@ Pg_exec(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) connStatus = conn->status; result = PQexec(conn, argv[2]); + + /* Transfer any notify events from libpq to Tcl event queue. */ + PgNotifyTransferEvents(connid); + if (result) { int rId = PgSetResultId(interp, argv[1], result); if (result->resultStatus == PGRES_COPY_IN || @@ -439,9 +443,11 @@ Pg_exec(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) /* error occurred during the query */ Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC); if (connStatus == CONNECTION_OK) { + /* Is this REALLY a good idea? I don't think so! */ PQreset(conn); if (conn->status == CONNECTION_OK) { result = PQexec(conn, argv[2]); + PgNotifyTransferEvents(connid); if (result) { int rId = PgSetResultId(interp, argv[1], result); if (result->resultStatus == PGRES_COPY_IN || @@ -699,7 +705,6 @@ Pg_lo_open(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR; } @@ -766,7 +771,6 @@ Pg_lo_close(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR; } @@ -804,7 +808,6 @@ Pg_lo_read(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR; } @@ -854,7 +857,6 @@ Pg_lo_write(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR; } @@ -900,7 +902,6 @@ Pg_lo_lseek(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR; } @@ -952,7 +953,6 @@ Pg_lo_creat(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR; } @@ -1008,7 +1008,6 @@ Pg_lo_tell(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR; } @@ -1043,7 +1042,6 @@ Pg_lo_unlink(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR; } @@ -1085,7 +1083,6 @@ Pg_lo_import(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR; } @@ -1125,7 +1122,6 @@ Pg_lo_export(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR; } @@ -1164,6 +1160,7 @@ Pg_lo_export(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) int Pg_select(ClientData cData, Tcl_Interp *interp, int argc, char **argv) { + Pg_ConnectionId *connid; PGconn *conn; PGresult *result; int r; @@ -1182,7 +1179,7 @@ Pg_select(ClientData cData, Tcl_Interp *interp, int argc, char **argv) return TCL_ERROR; } - conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL); + conn = PgGetConnectionId(interp, argv[1], &connid); if (conn == (PGconn *)NULL) { return TCL_ERROR; } @@ -1194,6 +1191,9 @@ Pg_select(ClientData cData, Tcl_Interp *interp, int argc, char **argv) return TCL_ERROR; } + /* Transfer any notify events from libpq to Tcl event queue. */ + PgNotifyTransferEvents(connid); + if ((info = (struct info_s *)ckalloc(sizeof(*info) * (ncols = PQnfields(result)))) == NULL) { Tcl_AppendResult(interp, "Not enough memory", 0); @@ -1248,145 +1248,139 @@ Pg_select(ClientData cData, Tcl_Interp *interp, int argc, char **argv) return TCL_OK; } +/*********************************** +Pg_listen + create or remove a callback request for notifies on a given name + + syntax: + pg_listen conn notifyname ?callbackcommand? + + With a fourth arg, creates or changes the callback command for + notifies on the given name; without, cancels the callback request. + + Callbacks can occur whenever Tcl is executing its event loop. + This is the normal idle loop in Tk; in plain tclsh applications, + vwait or update can be used to enter the Tcl event loop. +***********************************/ int Pg_listen(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) { - int new; - char *relname; - char *callback = NULL; + char *origrelname; + char *caserelname; + char *callback = NULL; + Pg_TclNotifies *notifies; Tcl_HashEntry *entry; Pg_ConnectionId *connid; PGconn *conn; PGresult *result; + int new; - if ((argc < 3) || (argc > 4)) { - Tcl_AppendResult(interp, "wrong # args, should be \"", - argv[0], " connection relname ?callback?\"", 0); - return TCL_ERROR; + if (argc < 3 || argc > 4) { + Tcl_AppendResult(interp, "wrong # args, should be \"", + argv[0], " connection relname ?callback?\"", 0); + return TCL_ERROR; } /* - * Get the command arguments. Note that relname will copied by - * Tcl_CreateHashEntry while callback must be allocated. + * Get the command arguments. Note that the relation name will be copied + * by Tcl_CreateHashEntry while the callback string must be allocated. */ conn = PgGetConnectionId(interp, argv[1], &connid); if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); - return TCL_ERROR; - } - relname = argv[2]; - if ((argc > 3) && *argv[3]) { - callback = (char *) ckalloc((unsigned) (strlen(argv[3])+1)); - strcpy(callback, argv[3]); + return TCL_ERROR; } - /* - * Set or update a callback for a relation; - */ - if (callback) { - entry = Tcl_CreateHashEntry(&(connid->notify_hash), relname, &new); - if (new) { - /* New callback, execute a listen command on the relation */ - char *cmd = (char *) ckalloc((unsigned) (strlen(argv[2])+8)); - sprintf(cmd, "LISTEN %s", relname); - result = PQexec(conn, cmd); - ckfree(cmd); - if (!result || (result->resultStatus != PGRES_COMMAND_OK)) { - /* Error occurred during the execution of command */ - if (result) PQclear(result); - ckfree(callback); - Tcl_DeleteHashEntry(entry); - Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC); - return TCL_ERROR; - } - PQclear(result); + /* + * LISTEN/NOTIFY do not preserve case unless the relation name is + * quoted. We have to do the same thing to ensure that we will find + * the desired pg_listen item. + */ + origrelname = argv[2]; + caserelname = (char *) ckalloc((unsigned) (strlen(origrelname) + 1)); + if (*origrelname == '"') { + /* Copy a quoted string without downcasing */ + strcpy(caserelname, origrelname + 1); + caserelname[strlen(caserelname) - 1] = '\0'; } else { - /* Free the old callback string */ - ckfree((char *) Tcl_GetHashValue(entry)); + /* Downcase it */ + char *rels = origrelname; + char *reld = caserelname; + while (*rels) { + *reld++ = tolower(*rels++); + } + *reld = '\0'; } - /* Store the new callback command */ - Tcl_SetHashValue(entry, callback); - } - /* - * Remove a callback for a relation. There is no way to - * un-listen a relation, simply remove the callback from - * the notify hash table. - */ - if (callback == NULL) { - entry = Tcl_FindHashEntry(&(connid->notify_hash), relname); - if (entry == NULL) { - Tcl_AppendResult(interp, "not listening on ", relname, 0); - return TCL_ERROR; - } - ckfree((char *) Tcl_GetHashValue(entry)); - Tcl_DeleteHashEntry(entry); + if ((argc > 3) && *argv[3]) { + callback = (char *) ckalloc((unsigned) (strlen(argv[3]) + 1)); + strcpy(callback, argv[3]); } - return TCL_OK; -} - -int -Pg_notifies(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) -{ - int count; - char buff[12]; - char *callback; - Tcl_HashEntry *entry; - Pg_ConnectionId *connid; - PGconn *conn; - PGresult *result; - PGnotify *notify; + /* Find or make a Pg_TclNotifies struct for this interp and connection */ - if (argc != 2) { - Tcl_AppendResult(interp, "wrong # args, should be \"", - argv[0], " connection\"", 0); - return TCL_ERROR; - } + for (notifies = connid->notify_list; notifies; notifies = notifies->next) { + if (notifies->interp == interp) + break; + } + if (notifies == NULL) { + notifies = (Pg_TclNotifies *) ckalloc(sizeof(Pg_TclNotifies)); + notifies->interp = interp; + Tcl_InitHashTable(¬ifies->notify_hash, TCL_STRING_KEYS); + notifies->next = connid->notify_list; + connid->notify_list = notifies; + Tcl_CallWhenDeleted(interp, PgNotifyInterpDelete, + (ClientData) notifies); + } /* - * Get the connection argument. + * Set or update a callback for a relation */ - conn = (PGconn*)PgGetConnectionId(interp, argv[1], &connid); - if (conn == (PGconn *)NULL) { - Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); - return TCL_ERROR; - } - - /* Execute an empty command to retrieve asynchronous notifications */ - result = PQexec(conn, " "); - if (result == NULL) { - /* Error occurred during the execution of command */ - Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC); - return TCL_ERROR; + if (callback) { + entry = Tcl_CreateHashEntry(¬ifies->notify_hash, caserelname, &new); + if (new) { + /* New callback, execute a listen command on the relation */ + char *cmd = (char *) ckalloc((unsigned) (strlen(origrelname)+8)); + sprintf(cmd, "LISTEN %s", origrelname); + result = PQexec(conn, cmd); + ckfree(cmd); + /* Transfer any notify events from libpq to Tcl event queue. */ + PgNotifyTransferEvents(connid); + if (!result || (result->resultStatus != PGRES_COMMAND_OK)) { + /* Error occurred during the execution of command */ + if (result) PQclear(result); + ckfree(callback); + ckfree(caserelname); + Tcl_DeleteHashEntry(entry); + Tcl_SetResult(interp, PQerrorMessage(conn), TCL_VOLATILE); + return TCL_ERROR; + } + PQclear(result); + } else { + /* Update, free the old callback string */ + ckfree((char *) Tcl_GetHashValue(entry)); + } + /* Store the new callback string */ + Tcl_SetHashValue(entry, callback); + /* Start the notify event source if it isn't already running */ + PgStartNotifyEventSource(connid); } - PQclear(result); /* - * Loop while there are pending notifies. + * Remove a callback for a relation. There is no way to + * un-listen a relation, so we simply remove the callback from + * the notify hash table. */ - for (count=0; count < 999; count++) { - /* See if there is a pending notification */ - notify = PQnotifies(conn); - if (notify == NULL) { - break; - } - entry = Tcl_FindHashEntry(&(connid->notify_hash), notify->relname); - if (entry != NULL) { - callback = (char*)Tcl_GetHashValue(entry); - if (callback) { - /* This should be a global eval, shouldn't it? */ - Tcl_Eval(interp, callback); - /* And what if there's an error. Bgerror should be called? */ - } - } - free(notify); + if (callback == NULL) { + entry = Tcl_FindHashEntry(¬ifies->notify_hash, caserelname); + if (entry == NULL) { + Tcl_AppendResult(interp, "not listening on ", origrelname, 0); + ckfree(caserelname); + return TCL_ERROR; + } + ckfree((char *) Tcl_GetHashValue(entry)); + Tcl_DeleteHashEntry(entry); } - /* - * Return the number of notifications processed. - */ - sprintf(buff, "%d", count); - Tcl_SetResult(interp, buff, TCL_VOLATILE); + ckfree(caserelname); return TCL_OK; } |