From: Randy Kunkee <kunkee@pluto.ops.NeoSoft.com>

It is my hope that the following "patches" to libpgtcl get included
in the next release.

See the update to the README file to get a full description of the changes.
This version of libpgtcl is completely interpreter-safe, implements the
database connection handle as a channel (no events yet, but will make it
a lot easier to do fileevents on it in the future), and supports the SQL
"copy table to stdout" and "copy table from stdin" commands, with the
I/O being from and to the connection handle.  The connection and result
handles are formatted in a way to make access to the tables more efficient.
This commit is contained in:
Marc G. Fournier 1998-03-15 08:03:00 +00:00
parent 609026bb6b
commit 6ac2528616
7 changed files with 1336 additions and 1298 deletions

View File

@ -7,7 +7,7 @@
#
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/Makefile.in,v 1.3 1998/02/13 05:09:57 scrappy Exp $
# $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/Makefile.in,v 1.4 1998/03/15 08:02:55 scrappy Exp $
#
#-------------------------------------------------------------------------
@ -37,7 +37,7 @@ ifeq ($(PORTNAME), linux)
install-shlib-dep := install-shlib
shlib := libpgtcl.so.1
CFLAGS += $(CFLAGS_SL)
LDFLAGS_SL = -shared -L $(SRCDIR)/interfaces/libpq -lpq
LDFLAGS_SL = -shared -L$(SRCDIR)/interfaces/libpq -lpq
endif
endif
@ -53,14 +53,14 @@ endif
ifeq ($(PORTNAME), i386_solaris)
install-shlib-dep := install-shlib
shlib := libpgtcl.so.1
LDFLAGS_SL = -G -z text -L $(SRCDIR)/interfaces/libpq -lpq
LDFLAGS_SL = -G -z text -L$(SRCDIR)/interfaces/libpq -lpq
CFLAGS += $(CFLAGS_SL)
endif
ifeq ($(PORTNAME), univel)
install-shlib-dep := install-shlib
shlib := libpgtcl.so.1
LDFLAGS_SL = -G -z text -L $(SRCDIR)/interfaces/libpq -lpq
LDFLAGS_SL = -G -z text -L$(SRCDIR)/interfaces/libpq -lpq
CFLAGS += $(CFLAGS_SL)
endif

View File

@ -1,6 +1,38 @@
libpgtcl is a library that implements Tcl commands for front-end clients
to interact with the PostgreSQL backend. See libpgtcl.doc for details.
libpgtcl is a library that implements Tcl commands for front-end
clients to interact with the Postgresql 6.3 (and perhaps later)
backends. See libpgtcl.doc for details.
For an example of how to build a new tclsh to use libpgtcl, see the
directory ../bin/pgtclsh
Note this version is modified by NeoSoft to have the following additional
features:
1. Postgres connections are a valid Tcl channel, and can therefore
be manipulated by the interp command (ie. shared or transfered).
A connection handle's results are transfered/shared with it.
(Result handles are NOT channels, though it was tempting). Note
that a "close $connection" is now functionally identical to a
"pg_disconnect $connection", although pg_connect must be used
to create a connection.
2. Result handles are changed in format: ${connection}.<result#>.
This just means for a connection 'pgtcl0', they look like pgtcl0.0,
pgtcl0.1, etc. Enforcing this syntax makes it easy to look up
the real pointer by indexing into an array associated with the
connection.
3. I/O routines are now defined for the connection handle. I/O to/from
the connection is only valid under certain circumstances: following
the execution of the queries "copy <table> from stdin" or
"copy <table> to stdout". In these cases, the result handle obtains
an intermediate status of "PGRES_COPY_IN" or "PGRES_COPY_OUT". The
programmer is then expected to use Tcl gets or read commands on the
database connection (not the result handle) to extract the copy data.
For copy outs, read until the standard EOF indication is encountered.
For copy ins, puts a single terminator (\.). The statement for this
would be
puts $conn "\\." or puts $conn {\.}
In either case (upon detecting the EOF or putting the `\.', the status
of the result handle will change to "PGRES_COMMAND_OK", and any further
I/O attempts will cause a Tcl error.

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtcl.c,v 1.9 1997/09/08 02:40:08 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtcl.c,v 1.10 1998/03/15 08:02:57 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,163 +23,122 @@
#include "pgtclId.h"
/*
* Pgtcl_Init
* initialization package for the PGLITE Tcl package
* Pgtcl_Init
* initialization package for the PGLITE Tcl package
*
*/
/*
* Tidy up forgotten postgres connection at Tcl_Exit
*/
static void
Pgtcl_AtExit(ClientData cData)
{
Pg_clientData *cd = (Pg_clientData *) cData;
Tcl_HashEntry *hent;
Tcl_HashSearch hsearch;
Pg_ConnectionId *connid;
PGconn *conn;
while ((hent = Tcl_FirstHashEntry(&(cd->dbh_hash), &hsearch)) != NULL)
{
connid = (Pg_ConnectionId *) Tcl_GetHashValue(hent);
conn = connid->conn;
PgDelConnectionId(cd, connid->id);
PQfinish(conn);
}
Tcl_DeleteHashTable(&(cd->dbh_hash));
Tcl_DeleteHashTable(&(cd->res_hash));
Tcl_DeleteHashTable(&(cd->notify_hash));
Tcl_DeleteExitHandler(Pgtcl_AtExit, cData);
}
/*
* Tidy up forgotten postgres connections on Interpreter deletion
*/
static void
Pgtcl_Shutdown(ClientData cData, Tcl_Interp * interp)
{
Pgtcl_AtExit(cData);
}
int
Pgtcl_Init(Tcl_Interp * interp)
Pgtcl_Init (Tcl_Interp *interp)
{
Pg_clientData *cd;
/* Create and initialize the client data area */
cd = (Pg_clientData *) ckalloc(sizeof(Pg_clientData));
Tcl_InitHashTable(&(cd->dbh_hash), TCL_STRING_KEYS);
Tcl_InitHashTable(&(cd->res_hash), TCL_STRING_KEYS);
Tcl_InitHashTable(&(cd->notify_hash), TCL_STRING_KEYS);
cd->dbh_count = 0L;
cd->res_count = 0L;
/* finish off the ChannelType struct. Much easier to do it here then
* 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)
Pg_ConnType.getFileProc = PgGetFileProc;
#endif
/* Arrange for tidy up when interpreter is deleted or Tcl exits */
Tcl_CallWhenDeleted(interp, Pgtcl_Shutdown, (ClientData) cd);
Tcl_CreateExitHandler(Pgtcl_AtExit, (ClientData) cd);
/* register all pgtcl commands */
Tcl_CreateCommand(interp,
"pg_conndefaults",
Pg_conndefaults,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
/* register all pgtcl commands */
Tcl_CreateCommand(interp,
"pg_conndefaults",
Pg_conndefaults,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_connect",
Pg_connect,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_connect",
Pg_connect,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_disconnect",
Pg_disconnect,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_exec",
Pg_exec,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_select",
Pg_select,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_result",
Pg_result,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_lo_open",
Pg_lo_open,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_lo_close",
Pg_lo_close,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_disconnect",
Pg_disconnect,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_lo_read",
Pg_lo_read,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_exec",
Pg_exec,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_lo_write",
Pg_lo_write,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_select",
Pg_select,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_lo_lseek",
Pg_lo_lseek,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_result",
Pg_result,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_lo_creat",
Pg_lo_creat,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_lo_open",
Pg_lo_open,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_lo_tell",
Pg_lo_tell,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_lo_close",
Pg_lo_close,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_lo_unlink",
Pg_lo_unlink,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_lo_read",
Pg_lo_read,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_lo_import",
Pg_lo_import,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_lo_export",
Pg_lo_export,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_listen",
Pg_listen,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_lo_write",
Pg_lo_write,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_notifies",
Pg_notifies,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp,
"pg_lo_lseek",
Pg_lo_lseek,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_PkgProvide(interp, "Pgtcl", "1.1");
Tcl_CreateCommand(interp,
"pg_lo_creat",
Pg_lo_creat,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_lo_tell",
Pg_lo_tell,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_lo_unlink",
Pg_lo_unlink,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_lo_import",
Pg_lo_import,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_lo_export",
Pg_lo_export,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_listen",
Pg_listen,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp,
"pg_notifies",
Pg_notifies,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_PkgProvide(interp, "Pgtcl", "1.0");
return TCL_OK;
return TCL_OK;
}
int
Pgtcl_SafeInit(Tcl_Interp * interp)
Pgtcl_SafeInit (Tcl_Interp *interp)
{
return Pgtcl_Init(interp);
return Pgtcl_Init(interp);
}

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pgtclCmds.h,v 1.8 1997/09/08 02:40:16 momjian Exp $
* $Id: pgtclCmds.h,v 1.9 1998/03/15 08:02:59 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,89 +18,69 @@
#include "libpq-fe.h"
#include "libpq/libpq-fs.h"
typedef struct Pg_clientData_s
{
Tcl_HashTable dbh_hash;
Tcl_HashTable res_hash;
Tcl_HashTable notify_hash;
long dbh_count;
long res_count;
} Pg_clientData;
#define RES_HARD_MAX 128
#define RES_START 16
typedef struct Pg_ConnectionId_s {
char id[32];
PGconn *conn;
int res_max; /* Max number of results allocated */
int res_hardmax; /* Absolute max to allow */
int res_count; /* Current count of active results */
int res_last; /* Optimize where to start looking */
int res_copy; /* Query result with active copy */
int res_copyStatus; /* Copying status */
PGresult **results; /* The results */
Tcl_HashTable notify_hash;
} Pg_ConnectionId;
typedef struct Pg_ConnectionId_s
{
char id[32];
PGconn *conn;
Tcl_HashTable res_hash;
} Pg_ConnectionId;
typedef struct Pg_ResultId_s
{
char id[32];
PGresult *result;
Pg_ConnectionId *connection;
} Pg_ResultId;
#define RES_COPY_NONE 0
#define RES_COPY_INPROGRESS 1
#define RES_COPY_FIN 2
/* **************************/
/* registered Tcl functions */
/* **************************/
extern int
Pg_conndefaults(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_connect(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_disconnect(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_exec(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_select(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_result(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_lo_open(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_lo_close(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_lo_read(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_lo_write(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_lo_lseek(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_lo_creat(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_lo_tell(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_lo_unlink(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
extern int
Pg_lo_import(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]);
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[]);
extern int Pg_conndefaults(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_connect(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_disconnect(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_exec(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_select(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_result(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_lo_open(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_lo_close(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_lo_read(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_lo_write(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_lo_lseek(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_lo_creat(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_lo_tell(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_lo_unlink(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int Pg_lo_import(
ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
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 */
#endif /*PGTCLCMDS_H*/

View File

@ -1,48 +1,173 @@
/*-------------------------------------------------------------------------
*
* pgtclId.c--
* useful routines to convert between strings and pointers
* Needed because everything in tcl is a string, but we want pointers
* to data structures
* useful routines to convert between strings and pointers
* Needed because everything in tcl is a string, but we want pointers
* to data structures
*
* ASSUMPTION: sizeof(long) >= sizeof(void*)
* ASSUMPTION: sizeof(long) >= sizeof(void*)
*
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.7 1998/02/26 04:44:53 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.8 1998/03/15 08:03:00 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <tcl.h>
#include "postgres.h"
#include "pgtclCmds.h"
#include "pgtclId.h"
int PgEndCopy(Pg_ConnectionId *connid, int *errorCodePtr)
{
connid->res_copyStatus = RES_COPY_NONE;
if (PQendcopy(connid->conn)) {
connid->results[connid->res_copy]->resultStatus = PGRES_BAD_RESPONSE;
connid->res_copy = -1;
*errorCodePtr = EIO;
return -1;
} else {
connid->results[connid->res_copy]->resultStatus = PGRES_COMMAND_OK;
connid->res_copy = -1;
return 0;
}
}
/*
* Create the Id for a new connection and hash it
* Called when reading data (via gets) for a copy <rel> to stdout
*/
int PgInputProc(DRIVER_INPUT_PROTO)
{
Pg_ConnectionId *connid;
PGconn *conn;
int c;
int avail;
connid = (Pg_ConnectionId *)cData;
conn = connid->conn;
if (connid->res_copy < 0 ||
connid->results[connid->res_copy]->resultStatus != PGRES_COPY_OUT) {
*errorCodePtr = EBUSY;
return -1;
}
if (connid->res_copyStatus == RES_COPY_FIN) {
return PgEndCopy(connid, errorCodePtr);
}
avail = bufSize;
while (avail > 0 &&
(c = pqGetc(conn->Pfin, conn->Pfdebug)) != EOF) {
/* fprintf(stderr, "%d: got char %c\n", bufSize-avail, c); */
*buf++ = c;
--avail;
if (c == '\n' && bufSize-avail > 3) {
if ((bufSize-avail == 3 || buf[-4] == '\n') &&
buf[-3] == '\\' && buf[-2] == '.') {
avail += 3;
connid->res_copyStatus = RES_COPY_FIN;
break;
}
}
}
/* fprintf(stderr, "returning %d chars\n", bufSize - avail); */
return bufSize - avail;
}
/*
* Called when writing data (via puts) for a copy <rel> from stdin
*/
int PgOutputProc(DRIVER_OUTPUT_PROTO)
{
Pg_ConnectionId *connid;
PGconn *conn;
connid = (Pg_ConnectionId *)cData;
conn = connid->conn;
if (connid->res_copy < 0 ||
connid->results[connid->res_copy]->resultStatus != PGRES_COPY_IN) {
*errorCodePtr = EBUSY;
return -1;
}
/*
fprintf(stderr, "PgOutputProc called: bufSize=%d: atend:%d <", bufSize,
strncmp(buf, "\\.\n", 3));
fwrite(buf, 1, bufSize, stderr);
fputs(">\n", stderr);
*/
fwrite(buf, 1, bufSize, conn->Pfout);
if (bufSize > 2 && strncmp(&buf[bufSize-3], "\\.\n", 3) == 0) {
/* fprintf(stderr,"checking closure\n"); */
fflush(conn->Pfout);
if (PgEndCopy(connid, errorCodePtr) == -1)
return -1;
}
return bufSize;
}
#if (TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 6)
Tcl_File
PgGetFileProc(ClientData cData, int direction)
{
return (Tcl_File)NULL;
}
#endif
Tcl_ChannelType Pg_ConnType = {
"pgsql", /* channel type */
NULL, /* blockmodeproc */
PgDelConnectionId, /* closeproc */
PgInputProc, /* inputproc */
PgOutputProc, /* outputproc */
/* Note the additional stuff can be left NULL,
or is initialized during a PgSetConnectionId */
};
/*
* Create and register a new channel for the connection
*/
void
PgSetConnectionId(Pg_clientData * cd, char *id, PGconn *conn)
PgSetConnectionId(Tcl_Interp *interp, PGconn *conn)
{
Tcl_HashEntry *hent;
Pg_ConnectionId *connid;
int hnew;
Tcl_Channel conn_chan;
Pg_ConnectionId *connid;
int i;
connid = (Pg_ConnectionId *) ckalloc(sizeof(Pg_ConnectionId));
connid->conn = conn;
Tcl_InitHashTable(&(connid->res_hash), TCL_STRING_KEYS);
sprintf(connid->id, "pgc%ld", cd->dbh_count++);
strcpy(id, connid->id);
connid = (Pg_ConnectionId *)ckalloc(sizeof(Pg_ConnectionId));
connid->conn = conn;
connid->res_count = 0;
connid->res_last = -1;
connid->res_max = RES_START;
connid->res_hardmax = RES_HARD_MAX;
connid->res_copy = -1;
connid->res_copyStatus = RES_COPY_NONE;
connid->results = (PGresult**)ckalloc(sizeof(PGresult*) * RES_START);
for (i = 0; i < RES_START; i++) connid->results[i] = NULL;
Tcl_InitHashTable(&connid->notify_hash, TCL_STRING_KEYS);
hent = Tcl_CreateHashEntry(&(cd->dbh_hash), connid->id, &hnew);
Tcl_SetHashValue(hent, (ClientData) connid);
sprintf(connid->id, "pgsql%d", fileno(conn->Pfout));
#if TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 5
conn_chan = Tcl_CreateChannel(&Pg_ConnType, connid->id, conn->Pfin, conn->Pfout, (ClientData)connid);
#else
conn_chan = Tcl_CreateChannel(&Pg_ConnType, connid->id, (ClientData)connid,
TCL_READABLE | TCL_WRITABLE);
#endif
Tcl_SetChannelOption(interp, conn_chan, "-buffering", "line");
Tcl_SetResult(interp, connid->id, TCL_VOLATILE);
Tcl_RegisterChannel(interp, conn_chan);
}
@ -50,19 +175,22 @@ PgSetConnectionId(Pg_clientData * cd, char *id, PGconn *conn)
* Get back the connection from the Id
*/
PGconn *
PgGetConnectionId(Pg_clientData * cd, char *id)
PgGetConnectionId(Tcl_Interp *interp, char *id, Pg_ConnectionId **connid_p)
{
Tcl_HashEntry *hent;
Pg_ConnectionId *connid;
Tcl_Channel conn_chan;
Pg_ConnectionId *connid;
hent = Tcl_FindHashEntry(&(cd->dbh_hash), id);
if (hent == NULL)
{
return (PGconn *) NULL;
}
conn_chan = Tcl_GetChannel(interp, id, 0);
if(conn_chan == NULL || Tcl_GetChannelType(conn_chan) != &Pg_ConnType) {
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, id, " is not a valid postgresql connection\n", 0);
return (PGconn *)NULL;
}
connid = (Pg_ConnectionId *) Tcl_GetHashValue(hent);
return connid->conn;
connid = (Pg_ConnectionId *)Tcl_GetChannelInstanceData(conn_chan);
if (connid_p)
*connid_p = connid;
return connid->conn;
}
@ -70,98 +198,139 @@ PgGetConnectionId(Pg_clientData * cd, char *id)
* Remove a connection Id from the hash table and
* close all portals the user forgot.
*/
void
PgDelConnectionId(Pg_clientData * cd, char *id)
int PgDelConnectionId(DRIVER_DEL_PROTO)
{
Tcl_HashEntry *hent;
Tcl_HashEntry *hent2;
Tcl_HashEntry *hent3;
Tcl_HashSearch hsearch;
Pg_ConnectionId *connid;
Pg_ResultId *resid;
Tcl_HashEntry *entry;
char *hval;
Tcl_HashSearch hsearch;
Pg_ConnectionId *connid;
int i;
hent = Tcl_FindHashEntry(&(cd->dbh_hash), id);
if (hent == NULL)
{
return;
}
connid = (Pg_ConnectionId *)cData;
connid = (Pg_ConnectionId *) Tcl_GetHashValue(hent);
for (i = 0; i < connid->res_max; i++) {
if (connid->results[i])
PQclear(connid->results[i]);
}
ckfree((void*)connid->results);
hent2 = Tcl_FirstHashEntry(&(connid->res_hash), &hsearch);
while (hent2 != NULL)
{
resid = (Pg_ResultId *) Tcl_GetHashValue(hent2);
PQclear(resid->result);
hent3 = Tcl_FindHashEntry(&(cd->res_hash), resid->id);
if (hent3 != NULL)
{
Tcl_DeleteHashEntry(hent3);
}
ckfree(resid);
hent2 = Tcl_NextHashEntry(&hsearch);
}
Tcl_DeleteHashTable(&(connid->res_hash));
Tcl_DeleteHashEntry(hent);
ckfree(connid);
for (entry = Tcl_FirstHashEntry(&(connid->notify_hash), &hsearch);
entry != NULL;
entry = Tcl_NextHashEntry(&hsearch))
{
hval = (char*)Tcl_GetHashValue(entry);
ckfree(hval);
}
Tcl_DeleteHashTable(&connid->notify_hash);
PQfinish(connid->conn);
ckfree((void*)connid);
return 0;
}
/*
* Create a new result Id and hash it
* Find a slot for a new result id. If the table is full, expand it by
* a factor of 2. However, do not expand past the hard max, as the client
* is probably just not clearing result handles like they should.
*/
void
PgSetResultId(Pg_clientData * cd, char *id, char *connid_c, PGresult *res)
int
PgSetResultId(Tcl_Interp *interp, char *connid_c, PGresult *res)
{
Tcl_HashEntry *hent;
Pg_ConnectionId *connid;
Pg_ResultId *resid;
int hnew;
Tcl_Channel conn_chan;
Pg_ConnectionId *connid;
int resid, i;
char buf[32];
hent = Tcl_FindHashEntry(&(cd->dbh_hash), connid_c);
if (hent == NULL)
conn_chan = Tcl_GetChannel(interp, connid_c, 0);
if(conn_chan == NULL)
return TCL_ERROR;
connid = (Pg_ConnectionId *)Tcl_GetChannelInstanceData(conn_chan);
for (resid = connid->res_last+1; resid != connid->res_last; resid++) {
if (resid == connid->res_max)
resid = 0;
if (!connid->results[resid])
{
connid = NULL;
connid->res_last = resid;
break;
}
else
{
connid = (Pg_ConnectionId *) Tcl_GetHashValue(hent);
}
if (connid->results[resid]) {
if (connid->res_max == connid->res_hardmax) {
Tcl_SetResult(interp, "hard limit on result handles reached",
TCL_STATIC);
return TCL_ERROR;
}
connid->res_last = connid->res_max;
resid = connid->res_max;
connid->res_max *= 2;
if (connid->res_max > connid->res_hardmax)
connid->res_max = connid->res_hardmax;
connid->results = (PGresult**)ckrealloc((void*)connid->results,
sizeof(PGresult*) * connid->res_max);
for (i = connid->res_last; i < connid->res_max; i++)
connid->results[i] = NULL;
}
resid = (Pg_ResultId *) ckalloc(sizeof(Pg_ResultId));
resid->result = res;
resid->connection = connid;
sprintf(resid->id, "pgr%ld", cd->res_count++);
strcpy(id, resid->id);
connid->results[resid] = res;
sprintf(buf, "%s.%d", connid_c, resid);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return resid;
}
hent = Tcl_CreateHashEntry(&(cd->res_hash), resid->id, &hnew);
Tcl_SetHashValue(hent, (ClientData) resid);
static int getresid(Tcl_Interp *interp, char *id, Pg_ConnectionId **connid_p)
{
Tcl_Channel conn_chan;
char *mark;
int resid;
Pg_ConnectionId *connid;
if (connid != NULL)
{
hent = Tcl_CreateHashEntry(&(connid->res_hash), resid->id, &hnew);
Tcl_SetHashValue(hent, (ClientData) resid);
}
if (!(mark = strchr(id, '.')))
return -1;
*mark = '\0';
conn_chan = Tcl_GetChannel(interp, id, 0);
*mark = '.';
if(conn_chan == NULL || Tcl_GetChannelType(conn_chan) != &Pg_ConnType) {
Tcl_SetResult(interp, "Invalid connection handle", TCL_STATIC);
return -1;
}
if (Tcl_GetInt(interp, mark + 1, &resid) == TCL_ERROR) {
Tcl_SetResult(interp, "Poorly formated result handle", TCL_STATIC);
return -1;
}
connid = (Pg_ConnectionId *)Tcl_GetChannelInstanceData(conn_chan);
if (resid < 0 || resid > connid->res_max || connid->results[resid] == NULL) {
Tcl_SetResult(interp, "Invalid result handle", TCL_STATIC);
return -1;
}
*connid_p = connid;
return resid;
}
/*
* Get back the result pointer from the Id
*/
PGresult *
PgGetResultId(Pg_clientData * cd, char *id)
PGresult *
PgGetResultId(Tcl_Interp *interp, char *id)
{
Tcl_HashEntry *hent;
Pg_ResultId *resid;
Pg_ConnectionId *connid;
int resid;
hent = Tcl_FindHashEntry(&(cd->res_hash), id);
if (hent == NULL)
{
return (PGresult *) NULL;
}
resid = (Pg_ResultId *) Tcl_GetHashValue(hent);
return resid->result;
if (!id)
return NULL;
resid = getresid(interp, id, &connid);
if (resid == -1)
return NULL;
return connid->results[resid];
}
@ -169,51 +338,41 @@ PgGetResultId(Pg_clientData * cd, char *id)
* Remove a result Id from the hash tables
*/
void
PgDelResultId(Pg_clientData * cd, char *id)
PgDelResultId(Tcl_Interp *interp, char *id)
{
Tcl_HashEntry *hent;
Tcl_HashEntry *hent2;
Pg_ResultId *resid;
Pg_ConnectionId *connid;
int resid;
hent = Tcl_FindHashEntry(&(cd->res_hash), id);
if (hent == NULL)
{
return;
}
resid = (Pg_ResultId *) Tcl_GetHashValue(hent);
if (resid->connection != NULL)
{
hent2 = Tcl_FindHashEntry(&(resid->connection->res_hash), id);
if (hent2 != NULL)
{
Tcl_DeleteHashEntry(hent2);
}
}
Tcl_DeleteHashEntry(hent);
ckfree(resid);
resid = getresid(interp, id, &connid);
if (resid == -1)
return;
connid->results[resid] = 0;
}
/*
* Get the connection Id from the result Id
*/
void
PgGetConnByResultId(Pg_clientData * cd, char *id, char *resid_c)
int
PgGetConnByResultId(Tcl_Interp *interp, char *resid_c)
{
Tcl_HashEntry *hent;
Pg_ResultId *resid;
char *mark;
Tcl_Channel conn_chan;
hent = Tcl_FindHashEntry(&(cd->res_hash), id);
if (hent == NULL)
{
return;
}
if (!(mark = strchr(resid_c, '.')))
goto error_out;
*mark = '\0';
conn_chan = Tcl_GetChannel(interp, resid_c, 0);
*mark = '.';
if(conn_chan && Tcl_GetChannelType(conn_chan) != &Pg_ConnType) {
Tcl_SetResult(interp, Tcl_GetChannelName(conn_chan), TCL_VOLATILE);
return TCL_OK;
}
resid = (Pg_ResultId *) Tcl_GetHashValue(hent);
if (resid->connection != NULL)
{
strcpy(id, resid->connection->id);
}
error_out:
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, resid_c, " is not a valid connection\n", 0);
return TCL_ERROR;
}

View File

@ -1,22 +1,47 @@
/*-------------------------------------------------------------------------
*
* pgtclId.h--
* useful routines to convert between strings and pointers
* Needed because everything in tcl is a string, but often, pointers
* to data structures are needed.
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pgtclId.h,v 1.5 1997/09/08 21:55:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
*
* pgtclId.h--
* useful routines to convert between strings and pointers
* Needed because everything in tcl is a string, but often, pointers
* to data structures are needed.
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pgtclId.h,v 1.6 1998/03/15 08:03:00 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
extern void PgSetConnectionId(Tcl_Interp *interp, PGconn *conn);
extern void PgSetConnectionId(Pg_clientData * cd, char *id, PGconn *conn);
extern PGconn *PgGetConnectionId(Pg_clientData * cd, char *id);
extern void PgDelConnectionId(Pg_clientData * cd, char *id);
extern void PgSetResultId(Pg_clientData * cd, char *id, char *connid, PGresult *res);
extern PGresult *PgGetResultId(Pg_clientData * cd, char *id);
extern void PgDelResultId(Pg_clientData * cd, char *id);
extern void PgGetConnByResultId(Pg_clientData * cd, char *id, char *resid);
#if (TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 5)
# 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, \
int bufSize, int *errorCodePtr
# define DRIVER_INPUT_PROTO ClientData cData, Tcl_File inFile, char *buf, \
int bufSize, int *errorCodePtr
#else
# define DRIVER_OUTPUT_PROTO ClientData cData, char *buf, int bufSize, \
int *errorCodePtr
# define DRIVER_INPUT_PROTO ClientData cData, char *buf, int bufSize, \
int *errorCodePtr
# define DRIVER_DEL_PROTO ClientData cData, Tcl_Interp *interp
#endif
extern PGconn *PgGetConnectionId(Tcl_Interp *interp, char *id, \
Pg_ConnectionId **);
extern PgDelConnectionId(DRIVER_DEL_PROTO);
extern int PgOutputProc(DRIVER_OUTPUT_PROTO);
extern PgInputProc(DRIVER_INPUT_PROTO);
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);
#if (TCL_MAJOR_VERSION < 8)
extern Tcl_File PgGetFileProc(ClientData cData, int direction);
#endif
extern Tcl_ChannelType Pg_ConnType;