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 # 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 install-shlib-dep := install-shlib
shlib := libpgtcl.so.1 shlib := libpgtcl.so.1
CFLAGS += $(CFLAGS_SL) CFLAGS += $(CFLAGS_SL)
LDFLAGS_SL = -shared -L $(SRCDIR)/interfaces/libpq -lpq LDFLAGS_SL = -shared -L$(SRCDIR)/interfaces/libpq -lpq
endif endif
endif endif
@ -53,14 +53,14 @@ endif
ifeq ($(PORTNAME), i386_solaris) ifeq ($(PORTNAME), i386_solaris)
install-shlib-dep := install-shlib install-shlib-dep := install-shlib
shlib := libpgtcl.so.1 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) CFLAGS += $(CFLAGS_SL)
endif endif
ifeq ($(PORTNAME), univel) ifeq ($(PORTNAME), univel)
install-shlib-dep := install-shlib install-shlib-dep := install-shlib
shlib := libpgtcl.so.1 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) CFLAGS += $(CFLAGS_SL)
endif endif

View File

@ -1,6 +1,38 @@
libpgtcl is a library that implements Tcl commands for front-end clients libpgtcl is a library that implements Tcl commands for front-end
to interact with the PostgreSQL backend. See libpgtcl.doc for details. 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 For an example of how to build a new tclsh to use libpgtcl, see the
directory ../bin/pgtclsh 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 * 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" #include "pgtclId.h"
/* /*
* Pgtcl_Init * Pgtcl_Init
* initialization package for the PGLITE Tcl package * 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 int
Pgtcl_Init(Tcl_Interp * interp) Pgtcl_Init (Tcl_Interp *interp)
{ {
Pg_clientData *cd;
/* Create and initialize the client data area */ /* finish off the ChannelType struct. Much easier to do it here then
cd = (Pg_clientData *) ckalloc(sizeof(Pg_clientData)); * to guess where it might be by position in the struct. This is needed
Tcl_InitHashTable(&(cd->dbh_hash), TCL_STRING_KEYS); * for Tcl7.6 and beyond, which have the getfileproc.
Tcl_InitHashTable(&(cd->res_hash), TCL_STRING_KEYS); */
Tcl_InitHashTable(&(cd->notify_hash), TCL_STRING_KEYS); #if (TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 6)
cd->dbh_count = 0L; Pg_ConnType.getFileProc = PgGetFileProc;
cd->res_count = 0L; #endif
/* Arrange for tidy up when interpreter is deleted or Tcl exits */ /* register all pgtcl commands */
Tcl_CallWhenDeleted(interp, Pgtcl_Shutdown, (ClientData) cd); Tcl_CreateCommand(interp,
Tcl_CreateExitHandler(Pgtcl_AtExit, (ClientData) cd); "pg_conndefaults",
Pg_conndefaults,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
/* register all pgtcl commands */ Tcl_CreateCommand(interp,
Tcl_CreateCommand(interp, "pg_connect",
"pg_conndefaults", Pg_connect,
Pg_conndefaults, (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_connect", "pg_disconnect",
Pg_connect, Pg_disconnect,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (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, Tcl_CreateCommand(interp,
"pg_disconnect", "pg_lo_read",
Pg_disconnect, Pg_lo_read,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_exec", "pg_lo_write",
Pg_exec, Pg_lo_write,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_select", "pg_lo_lseek",
Pg_select, Pg_lo_lseek,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_result", "pg_lo_creat",
Pg_result, Pg_lo_creat,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_open", "pg_lo_tell",
Pg_lo_open, Pg_lo_tell,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_close", "pg_lo_unlink",
Pg_lo_close, Pg_lo_unlink,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_read", "pg_lo_import",
Pg_lo_read, Pg_lo_import,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (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, Tcl_CreateCommand(interp,
"pg_lo_write", "pg_notifies",
Pg_lo_write, Pg_notifies,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_PkgProvide(interp, "Pgtcl", "1.1");
"pg_lo_lseek",
Pg_lo_lseek,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp, return TCL_OK;
"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;
} }
int 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 * 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-fe.h"
#include "libpq/libpq-fs.h" #include "libpq/libpq-fs.h"
typedef struct Pg_clientData_s #define RES_HARD_MAX 128
{ #define RES_START 16
Tcl_HashTable dbh_hash;
Tcl_HashTable res_hash; typedef struct Pg_ConnectionId_s {
Tcl_HashTable notify_hash; char id[32];
long dbh_count; PGconn *conn;
long res_count; int res_max; /* Max number of results allocated */
} Pg_clientData; 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 #define RES_COPY_NONE 0
{ #define RES_COPY_INPROGRESS 1
char id[32]; #define RES_COPY_FIN 2
PGconn *conn;
Tcl_HashTable res_hash;
} Pg_ConnectionId;
typedef struct Pg_ResultId_s
{
char id[32];
PGresult *result;
Pg_ConnectionId *connection;
} Pg_ResultId;
/* **************************/ /* **************************/
/* registered Tcl functions */ /* registered Tcl functions */
/* **************************/ /* **************************/
extern int extern int Pg_conndefaults(
Pg_conndefaults( ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); extern int Pg_connect(
extern int ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
Pg_connect( extern int Pg_disconnect(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int extern int Pg_exec(
Pg_disconnect( ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); extern int Pg_select(
extern int ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
Pg_exec( extern int Pg_result(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int extern int Pg_lo_open(
Pg_select( ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); extern int Pg_lo_close(
extern int ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
Pg_result( extern int Pg_lo_read(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int extern int Pg_lo_write(
Pg_lo_open( ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); extern int Pg_lo_lseek(
extern int ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
Pg_lo_close( extern int Pg_lo_creat(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int extern int Pg_lo_tell(
Pg_lo_read( ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); extern int Pg_lo_unlink(
extern int ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
Pg_lo_write( extern int Pg_lo_import(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
extern int extern int Pg_lo_export(
Pg_lo_lseek( ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); extern int Pg_listen(
extern int ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
Pg_lo_creat( extern int Pg_notifies(
ClientData cData, Tcl_Interp * interp, int argc, char *argv[]); 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-- * pgtclId.c--
* useful routines to convert between strings and pointers * useful routines to convert between strings and pointers
* Needed because everything in tcl is a string, but we want pointers * Needed because everything in tcl is a string, but we want pointers
* to data structures * to data structures
* *
* ASSUMPTION: sizeof(long) >= sizeof(void*) * ASSUMPTION: sizeof(long) >= sizeof(void*)
* *
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * 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 <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h>
#include <tcl.h> #include <tcl.h>
#include "postgres.h" #include "postgres.h"
#include "pgtclCmds.h" #include "pgtclCmds.h"
#include "pgtclId.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 void
PgSetConnectionId(Pg_clientData * cd, char *id, PGconn *conn) PgSetConnectionId(Tcl_Interp *interp, PGconn *conn)
{ {
Tcl_HashEntry *hent; Tcl_Channel conn_chan;
Pg_ConnectionId *connid; Pg_ConnectionId *connid;
int hnew; int i;
connid = (Pg_ConnectionId *) ckalloc(sizeof(Pg_ConnectionId)); connid = (Pg_ConnectionId *)ckalloc(sizeof(Pg_ConnectionId));
connid->conn = conn; connid->conn = conn;
Tcl_InitHashTable(&(connid->res_hash), TCL_STRING_KEYS); connid->res_count = 0;
sprintf(connid->id, "pgc%ld", cd->dbh_count++); connid->res_last = -1;
strcpy(id, connid->id); 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); sprintf(connid->id, "pgsql%d", fileno(conn->Pfout));
Tcl_SetHashValue(hent, (ClientData) connid);
#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 * Get back the connection from the Id
*/ */
PGconn * PGconn *
PgGetConnectionId(Pg_clientData * cd, char *id) PgGetConnectionId(Tcl_Interp *interp, char *id, Pg_ConnectionId **connid_p)
{ {
Tcl_HashEntry *hent; Tcl_Channel conn_chan;
Pg_ConnectionId *connid; Pg_ConnectionId *connid;
hent = Tcl_FindHashEntry(&(cd->dbh_hash), id); conn_chan = Tcl_GetChannel(interp, id, 0);
if (hent == NULL) if(conn_chan == NULL || Tcl_GetChannelType(conn_chan) != &Pg_ConnType) {
{ Tcl_ResetResult(interp);
return (PGconn *) NULL; Tcl_AppendResult(interp, id, " is not a valid postgresql connection\n", 0);
} return (PGconn *)NULL;
}
connid = (Pg_ConnectionId *) Tcl_GetHashValue(hent); connid = (Pg_ConnectionId *)Tcl_GetChannelInstanceData(conn_chan);
return connid->conn; 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 * Remove a connection Id from the hash table and
* close all portals the user forgot. * close all portals the user forgot.
*/ */
void int PgDelConnectionId(DRIVER_DEL_PROTO)
PgDelConnectionId(Pg_clientData * cd, char *id)
{ {
Tcl_HashEntry *hent; Tcl_HashEntry *entry;
Tcl_HashEntry *hent2; char *hval;
Tcl_HashEntry *hent3; Tcl_HashSearch hsearch;
Tcl_HashSearch hsearch; Pg_ConnectionId *connid;
Pg_ConnectionId *connid; int i;
Pg_ResultId *resid;
hent = Tcl_FindHashEntry(&(cd->dbh_hash), id); connid = (Pg_ConnectionId *)cData;
if (hent == NULL)
{
return;
}
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); for (entry = Tcl_FirstHashEntry(&(connid->notify_hash), &hsearch);
while (hent2 != NULL) entry != NULL;
{ entry = Tcl_NextHashEntry(&hsearch))
resid = (Pg_ResultId *) Tcl_GetHashValue(hent2); {
PQclear(resid->result); hval = (char*)Tcl_GetHashValue(entry);
hent3 = Tcl_FindHashEntry(&(cd->res_hash), resid->id); ckfree(hval);
if (hent3 != NULL) }
{
Tcl_DeleteHashEntry(hent3); Tcl_DeleteHashTable(&connid->notify_hash);
} PQfinish(connid->conn);
ckfree(resid); ckfree((void*)connid);
hent2 = Tcl_NextHashEntry(&hsearch); return 0;
}
Tcl_DeleteHashTable(&(connid->res_hash));
Tcl_DeleteHashEntry(hent);
ckfree(connid);
} }
/* /*
* 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 int
PgSetResultId(Pg_clientData * cd, char *id, char *connid_c, PGresult *res) PgSetResultId(Tcl_Interp *interp, char *connid_c, PGresult *res)
{ {
Tcl_HashEntry *hent; Tcl_Channel conn_chan;
Pg_ConnectionId *connid; Pg_ConnectionId *connid;
Pg_ResultId *resid; int resid, i;
int hnew; 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)); connid->results[resid] = res;
resid->result = res; sprintf(buf, "%s.%d", connid_c, resid);
resid->connection = connid; Tcl_SetResult(interp, buf, TCL_VOLATILE);
sprintf(resid->id, "pgr%ld", cd->res_count++); return resid;
strcpy(id, resid->id); }
hent = Tcl_CreateHashEntry(&(cd->res_hash), resid->id, &hnew); static int getresid(Tcl_Interp *interp, char *id, Pg_ConnectionId **connid_p)
Tcl_SetHashValue(hent, (ClientData) resid); {
Tcl_Channel conn_chan;
char *mark;
int resid;
Pg_ConnectionId *connid;
if (connid != NULL) if (!(mark = strchr(id, '.')))
{ return -1;
hent = Tcl_CreateHashEntry(&(connid->res_hash), resid->id, &hnew); *mark = '\0';
Tcl_SetHashValue(hent, (ClientData) resid); 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 * Get back the result pointer from the Id
*/ */
PGresult * PGresult *
PgGetResultId(Pg_clientData * cd, char *id) PgGetResultId(Tcl_Interp *interp, char *id)
{ {
Tcl_HashEntry *hent; Pg_ConnectionId *connid;
Pg_ResultId *resid; int resid;
hent = Tcl_FindHashEntry(&(cd->res_hash), id); if (!id)
if (hent == NULL) return NULL;
{ resid = getresid(interp, id, &connid);
return (PGresult *) NULL; if (resid == -1)
} return NULL;
return connid->results[resid];
resid = (Pg_ResultId *) Tcl_GetHashValue(hent);
return resid->result;
} }
@ -169,51 +338,41 @@ PgGetResultId(Pg_clientData * cd, char *id)
* Remove a result Id from the hash tables * Remove a result Id from the hash tables
*/ */
void void
PgDelResultId(Pg_clientData * cd, char *id) PgDelResultId(Tcl_Interp *interp, char *id)
{ {
Tcl_HashEntry *hent; Pg_ConnectionId *connid;
Tcl_HashEntry *hent2; int resid;
Pg_ResultId *resid;
hent = Tcl_FindHashEntry(&(cd->res_hash), id); resid = getresid(interp, id, &connid);
if (hent == NULL) if (resid == -1)
{ return;
return; connid->results[resid] = 0;
}
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);
} }
/* /*
* Get the connection Id from the result Id * Get the connection Id from the result Id
*/ */
void int
PgGetConnByResultId(Pg_clientData * cd, char *id, char *resid_c) PgGetConnByResultId(Tcl_Interp *interp, char *resid_c)
{ {
Tcl_HashEntry *hent; char *mark;
Pg_ResultId *resid; Tcl_Channel conn_chan;
hent = Tcl_FindHashEntry(&(cd->res_hash), id); if (!(mark = strchr(resid_c, '.')))
if (hent == NULL) goto error_out;
{ *mark = '\0';
return; 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); error_out:
if (resid->connection != NULL) Tcl_ResetResult(interp);
{ Tcl_AppendResult(interp, resid_c, " is not a valid connection\n", 0);
strcpy(id, resid->connection->id); return TCL_ERROR;
}
} }

View File

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