Commit 58389183 authored by Bruce Momjian's avatar Bruce Momjian

The attached patch modifies libpgtcl per previous discussion: the

pg_notifies statement is eliminated, and callbacks defined by
pg_listen are instead invoked automatically from the Tcl idle loop
whenever a NOTIFY message is received.

I have done only cursory testing, so there may be problems still
lurking (particularly on non-Unix machines?).  But it seems to
work.

Patch is against today's cvs sources.  Note that this will not work
with the 6.3.2 release since it depends on the new libpq.

The diffs are a bit large so I've gzipped them.  A patch to update
libpgtcl.sgml is included too.

			regards, tom lane
parent 693b156a
......@@ -3,11 +3,8 @@
<Para>
pgtcl is a tcl package for front-end programs to interface with <ProductName>Postgres</ProductName>
backends. pgtcl does not use the libpq library but communicates to
the backend directly via the frontend-backend protocol. Thus, it is
more efficient than previous postgres->tcl bindings which are layered
on top of libpq. In addition, pgtcl can handle multiple backend
connections from a single frontend application.
backends. It makes most of the functionality of libpq available to
tcl scripts.
</Para>
<Para>
......@@ -42,17 +39,25 @@ the standard Unix file system interface.
<ENTRY>pg_disconnect</ENTRY>
<ENTRY>closes a connection</ENTRY>
</ROW>
<ROW>
<ENTRY>pg_conndefaults</ENTRY>
<ENTRY>get connection options and their defaults</ENTRY>
</ROW>
<ROW>
<ENTRY>pg_exec</ENTRY>
<ENTRY>send a query to the backend</ENTRY>
</ROW>
<ROW>
<ENTRY>pg_result</ENTRY>
<ENTRY>manipulate the results of a query</ENTRY>
</ROW>
<ROW>
<ENTRY>pg_select</ENTRY>
<ENTRY>loop over the result of a select statement</ENTRY>
</ROW>
<ROW>
<ENTRY>pg_result</ENTRY>
<ENTRY>manipulate the results of a query</ENTRY>
<ENTRY>pg_listen</ENTRY>
<ENTRY>establish a callback for NOTIFY messages</ENTRY>
</ROW>
<ROW>
......@@ -101,8 +106,7 @@ the standard Unix file system interface.
</Para>
<Para>
Some commands equivalent to libpq commands are provided for connection
and query operations.
These commands are described further on subsequent pages.
</Para>
<Para>
......@@ -142,7 +146,7 @@ proc getDBs { {host "localhost"} {port "5432"} } {
</Sect1>
<Sect1>
<Title>Reference Information</Title>
<Title>pgtcl Command Reference Information</Title>
<REFENTRY ID="PGTCL-PGCONNECT-1">
<REFMETA>
......@@ -235,7 +239,7 @@ pg_connect <REPLACEABLE CLASS="PARAMETER">dbName</REPLACEABLE> <OPTIONAL>-host <
<LISTITEM>
<PARA>
The return result is either an error message or a handle for a database
connection. Handles start with the prefix "pgp"
connection. Handles start with the prefix "pgsql"
</PARA>
</LISTITEM>
</VARLISTENTRY>
......@@ -414,7 +418,114 @@ pg_exec <REPLACEABLE CLASS="PARAMETER">dbHandle</REPLACEABLE> <REPLACEABLE CLASS
</TITLE>
<PARA>
<FUNCTION>pg_exec</FUNCTION> submits a query to the <ProductName>Postgres</ProductName> backend and returns a result.
Handles start with the prefix "pgp".
Query result handles start with the connection handle and add a period
and a result number.
</PARA>
</REFSECT1>
<REFENTRY ID="PGTCL-PGLISTEN-1">
<REFMETA>
<REFENTRYTITLE>pg_listen</REFENTRYTITLE>
<REFMISCINFO>PGTCL - Asynchronous Notify</REFMISCINFO>
</REFMETA>
<REFNAMEDIV>
<REFNAME>pg_listen
</REFNAME>
<REFPURPOSE>sets or changes a callback for asynchronous NOTIFY messages
</REFPURPOSE>
<INDEXTERM ID="IX-PGTCL-PGLISTEN-1"><PRIMARY>pgtcl</PRIMARY><SECONDARY>notify</SECONDARY></INDEXTERM>
<INDEXTERM ID="IX-PGTCL-PGLISTEN-2"><PRIMARY>notify</PRIMARY></INDEXTERM>
</REFNAMEDIV>
<REFSYNOPSISDIV>
<REFSYNOPSISDIVINFO>
<DATE>1998-5-22</DATE>
</REFSYNOPSISDIVINFO>
<SYNOPSIS>
pg_listen <REPLACEABLE CLASS="PARAMETER">dbHandle</REPLACEABLE> <REPLACEABLE CLASS="PARAMETER">notifyName</REPLACEABLE> <REPLACEABLE CLASS="PARAMETER">callbackCommand</REPLACEABLE>
</SYNOPSIS>
<REFSECT2 ID="R2-PGTCL-PGLISTEN-1">
<REFSECT2INFO>
<DATE>1998-5-22</DATE>
</REFSECT2INFO>
<TITLE>Inputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
<REPLACEABLE CLASS="PARAMETER">dbHandle</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>Specifies a valid database handle.
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
<REPLACEABLE CLASS="PARAMETER">notifyName</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>Specifies the notification name to start or stop listening to.
</PARA>
</LISTITEM>
</VARLISTENTRY>
<VARLISTENTRY>
<TERM>
<REPLACEABLE CLASS="PARAMETER">callbackCommand</REPLACEABLE>
</TERM>
<LISTITEM>
<PARA>If present and not empty, provides the command string to execute
when a matching notification arrives.
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
<REFSECT2 ID="R2-PGTCL-PGLISTEN-2">
<REFSECT2INFO>
<DATE>1998-5-22</DATE>
</REFSECT2INFO>
<TITLE>Outputs
</TITLE>
<VARIABLELIST>
<VARLISTENTRY>
<TERM>
None
</TERM>
<LISTITEM>
<PARA>
</PARA>
</LISTITEM>
</VARLISTENTRY>
</VARIABLELIST>
</REFSECT2>
</REFSYNOPSISDIV>
<REFSECT1 ID="R1-PGTCL-PGLISTEN-1">
<REFSECT1INFO>
<DATE>1998-5-22</DATE>
</REFSECT1INFO>
<TITLE>Description
</TITLE>
<PARA><FUNCTION>pg_listen</FUNCTION> creates, changes, or cancels a request
to listen for asynchronous NOTIFY messages from the
<ProductName>Postgres</ProductName> backend. With a callbackCommand
parameter, the request is established, or the command string of an already
existing request is replaced. With no callbackCommand parameter, a prior
request is canceled.
</PARA>
After a <PARA><FUNCTION>pg_listen</FUNCTION> request is established,
the specified command string is executed whenever a NOTIFY message bearing
the given name arrives from the backend. This occurs when any
<ProductName>Postgres</ProductName> client application issues a NOTIFY command
referencing that name. (Note that the name can be, but does not have to be,
that of an existing relation in the database.)
The command string is executed from the Tcl idle loop. That is the normal
idle state of an application written with Tk. In non-Tk Tcl shells, you can
execute <FUNCTION>update</FUNCTION> or <FUNCTION>vwait</FUNCTION> to cause
the idle loop to be entered.
</PARA>
</REFSECT1>
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtcl.c,v 1.10 1998/03/15 08:02:57 scrappy Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtcl.c,v 1.11 1998/06/16 04:10:15 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -36,7 +36,7 @@ Pgtcl_Init (Tcl_Interp *interp)
* to guess where it might be by position in the struct. This is needed
* for Tcl7.6 and beyond, which have the getfileproc.
*/
#if (TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 6)
#if HAVE_TCL_GETFILEPROC
Pg_ConnType.getFileProc = PgGetFileProc;
#endif
......@@ -126,12 +126,7 @@ Pgtcl_Init (Tcl_Interp *interp)
Pg_listen,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_notifies",
Pg_notifies,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_PkgProvide(interp, "Pgtcl", "1.1");
Tcl_PkgProvide(interp, "Pgtcl", "1.2");
return TCL_OK;
}
......
This diff is collapsed.
......@@ -5,7 +5,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pgtclCmds.h,v 1.9 1998/03/15 08:02:59 scrappy Exp $
* $Id: pgtclCmds.h,v 1.10 1998/06/16 04:10:17 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -14,13 +14,29 @@
#define PGTCLCMDS_H
#include "tcl.h"
#include "libpq/pqcomm.h"
#include "libpq-fe.h"
#include "libpq/libpq-fs.h"
#define RES_HARD_MAX 128
#define RES_START 16
/*
* Each Pg_ConnectionId has a list of Pg_TclNotifies structs, one for each
* Tcl interpreter that has executed any pg_listens on the connection.
* We need this arrangement to be able to clean up if an interpreter is
* deleted while the connection remains open. A free side benefit is that
* multiple interpreters can be registered to listen for the same notify
* name. (All their callbacks will be called, but in an unspecified order.)
*/
typedef struct Pg_TclNotifies_s {
struct Pg_TclNotifies_s *next; /* list link */
Tcl_Interp *interp; /* This Tcl interpreter */
/* NB: if interp == NULL, the interpreter is gone but we haven't
* yet got round to deleting the Pg_TclNotifies structure.
*/
Tcl_HashTable notify_hash; /* Active pg_listen requests */
} Pg_TclNotifies;
typedef struct Pg_ConnectionId_s {
char id[32];
PGconn *conn;
......@@ -31,10 +47,11 @@ typedef struct Pg_ConnectionId_s {
int res_copy; /* Query result with active copy */
int res_copyStatus; /* Copying status */
PGresult **results; /* The results */
Tcl_HashTable notify_hash;
} Pg_ConnectionId;
Pg_TclNotifies *notify_list; /* head of list of notify info */
int notifier_running; /* notify event source is live */
} Pg_ConnectionId;
#define RES_COPY_NONE 0
#define RES_COPY_INPROGRESS 1
......@@ -78,9 +95,5 @@ extern int Pg_lo_export(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_listen(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_notifies(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
#endif /*PGTCLCMDS_H*/
This diff is collapsed.
......@@ -8,14 +8,15 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pgtclId.h,v 1.6 1998/03/15 08:03:00 scrappy Exp $
* $Id: pgtclId.h,v 1.7 1998/06/16 04:10:17 momjian Exp $
*
*-------------------------------------------------------------------------
*/
extern void PgSetConnectionId(Tcl_Interp *interp, PGconn *conn);
#if (TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 5)
#if TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 5
/* Only Tcl 7.5 had drivers with this signature */
# define DRIVER_DEL_PROTO ClientData cData, Tcl_Interp *interp, \
Tcl_File inFile, Tcl_File outFile
# define DRIVER_OUTPUT_PROTO ClientData cData, Tcl_File outFile, char *buf, \
......@@ -23,6 +24,7 @@ extern void PgSetConnectionId(Tcl_Interp *interp, PGconn *conn);
# define DRIVER_INPUT_PROTO ClientData cData, Tcl_File inFile, char *buf, \
int bufSize, int *errorCodePtr
#else
/* Tcl 7.6 and beyond use this signature */
# define DRIVER_OUTPUT_PROTO ClientData cData, char *buf, int bufSize, \
int *errorCodePtr
# define DRIVER_INPUT_PROTO ClientData cData, char *buf, int bufSize, \
......@@ -39,8 +41,19 @@ extern int PgSetResultId(Tcl_Interp *interp, char *connid, PGresult *res);
extern PGresult *PgGetResultId(Tcl_Interp *interp, char *id);
extern void PgDelResultId(Tcl_Interp *interp, char *id);
extern int PgGetConnByResultId(Tcl_Interp *interp, char *resid);
extern void PgStartNotifyEventSource(Pg_ConnectionId *connid);
extern void PgStopNotifyEventSource(Pg_ConnectionId *connid);
extern void PgNotifyTransferEvents(Pg_ConnectionId *connid);
extern void PgNotifyInterpDelete(ClientData clientData, Tcl_Interp *interp);
#if (TCL_MAJOR_VERSION < 8)
/* GetFileProc is needed in Tcl 7.6 and later */
#if (TCL_MAJOR_VERSION * 100 + TCL_MINOR_VERSION) >= 706
#define HAVE_TCL_GETFILEPROC 1
#else
#define HAVE_TCL_GETFILEPROC 0
#endif
#if HAVE_TCL_GETFILEPROC
extern Tcl_File PgGetFileProc(ClientData cData, int direction);
#endif
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment