Commit 6ac25286 authored by Marc G. Fournier's avatar Marc G. Fournier

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.
parent 609026bb
...@@ -7,7 +7,7 @@ ...@@ -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) ...@@ -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 ...@@ -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
......
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.
...@@ -9,7 +9,7 @@ ...@@ -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 @@ ...@@ -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;
/* finish off the ChannelType struct. Much easier to do it here then
/* Create and initialize the client data area */ * to guess where it might be by position in the struct. This is needed
cd = (Pg_clientData *) ckalloc(sizeof(Pg_clientData)); * for Tcl7.6 and beyond, which have the getfileproc.
Tcl_InitHashTable(&(cd->dbh_hash), TCL_STRING_KEYS); */
Tcl_InitHashTable(&(cd->res_hash), TCL_STRING_KEYS); #if (TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 6)
Tcl_InitHashTable(&(cd->notify_hash), TCL_STRING_KEYS); Pg_ConnType.getFileProc = PgGetFileProc;
cd->dbh_count = 0L; #endif
cd->res_count = 0L;
/* register all pgtcl commands */
/* Arrange for tidy up when interpreter is deleted or Tcl exits */ Tcl_CreateCommand(interp,
Tcl_CallWhenDeleted(interp, Pgtcl_Shutdown, (ClientData) cd); "pg_conndefaults",
Tcl_CreateExitHandler(Pgtcl_AtExit, (ClientData) cd); Pg_conndefaults,
(ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
/* register all pgtcl commands */
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_conndefaults", "pg_connect",
Pg_conndefaults, Pg_connect,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (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, Tcl_CreateCommand(interp,
"pg_disconnect", "pg_exec",
Pg_disconnect, Pg_exec,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_exec", "pg_select",
Pg_exec, Pg_select,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_select", "pg_result",
Pg_select, Pg_result,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_result", "pg_lo_open",
Pg_result, Pg_lo_open,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_open", "pg_lo_close",
Pg_lo_open, Pg_lo_close,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_close", "pg_lo_read",
Pg_lo_close, Pg_lo_read,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_read", "pg_lo_write",
Pg_lo_read, Pg_lo_write,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_write", "pg_lo_lseek",
Pg_lo_write, Pg_lo_lseek,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_lseek", "pg_lo_creat",
Pg_lo_lseek, Pg_lo_creat,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_creat", "pg_lo_tell",
Pg_lo_creat, Pg_lo_tell,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_tell", "pg_lo_unlink",
Pg_lo_tell, Pg_lo_unlink,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_unlink", "pg_lo_import",
Pg_lo_unlink, Pg_lo_import,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_import", "pg_lo_export",
Pg_lo_import, Pg_lo_export,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_lo_export", "pg_listen",
Pg_lo_export, Pg_listen,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_CreateCommand(interp,
"pg_listen", "pg_notifies",
Pg_listen, Pg_notifies,
(ClientData) cd, (Tcl_CmdDeleteProc *) NULL); (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
Tcl_CreateCommand(interp, Tcl_PkgProvide(interp, "Pgtcl", "1.1");
"pg_notifies",
Pg_notifies, return TCL_OK;
(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);
} }
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.21 1998/02/26 04:44:48 momjian Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.22 1998/03/15 08:02:58 scrappy Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -240,258 +240,255 @@ tcl_value(char *value) ...@@ -240,258 +240,255 @@ tcl_value(char *value)
/********************************** /**********************************
* pg_conndefaults * pg_conndefaults
syntax: syntax:
pg_conndefaults pg_conndefaults
the return result is a list describing the possible options and their the return result is a list describing the possible options and their
current default values for a call to pg_connect with the new -conninfo current default values for a call to pg_connect with the new -conninfo
syntax. Each entry in the list is a sublist of the format: syntax. Each entry in the list is a sublist of the format:
{optname label dispchar dispsize value} {optname label dispchar dispsize value}
**********************************/ **********************************/
int int
Pg_conndefaults(ClientData cData, Tcl_Interp * interp, int argc, char **argv) Pg_conndefaults(ClientData cData, Tcl_Interp *interp, int argc, char **argv)
{ {
PQconninfoOption *option; PQconninfoOption *option;
char buf[8192]; char buf[8192];
Tcl_ResetResult(interp); Tcl_ResetResult(interp);
for (option = PQconndefaults(); option->keyword != NULL; option++) for(option = PQconndefaults(); option->keyword != NULL; option++) {
{ if(option->val == NULL) {
if (option->val == NULL) option->val = "";
{ }
option->val = ""; sprintf(buf, "{%s} {%s} {%s} %d {%s}",
} option->keyword,
sprintf(buf, "{%s} {%s} {%s} %d {%s}", option->label,
option->keyword, option->dispchar,
option->label, option->dispsize,
option->dispchar, option->val);
option->dispsize, Tcl_AppendElement(interp, buf);
option->val); }
Tcl_AppendElement(interp, buf);
} return TCL_OK;
return TCL_OK;
} }
/********************************** /**********************************
* pg_connect * pg_connect
make a connection to a backend. make a connection to a backend.
syntax: syntax:
pg_connect dbName [-host hostName] [-port portNumber] [-tty pqtty]] pg_connect dbName [-host hostName] [-port portNumber] [-tty pqtty]]
the return result is either an error message or a handle for a database 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 "pgp"
**********************************/ **********************************/
int int
Pg_connect(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_connect(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; char *pghost = NULL;
char *pghost = NULL; char *pgtty = NULL;
char *pgtty = NULL; char *pgport = NULL;
char *pgport = NULL; char *pgoptions = NULL;
char *pgoptions = NULL; char *dbName;
char *dbName; int i;
int i; PGconn *conn;
PGconn *conn;
if (argc == 1) {
if (argc == 1) Tcl_AppendResult(interp, "pg_connect: database name missing\n", 0);
{ Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]\n", 0);
Tcl_AppendResult(interp, "pg_connect: database name missing\n", 0); Tcl_AppendResult(interp, "pg_connect -conninfo <conninfo-string>", 0);
Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]\n", 0); return TCL_ERROR;
Tcl_AppendResult(interp, "pg_connect -conninfo <conninfo-string>", 0);
return TCL_ERROR; }
if (!strcmp("-conninfo", argv[1])) {
/*
* Establish a connection using the new PQconnectdb() interface
*/
if (argc != 3) {
Tcl_AppendResult(interp, "pg_connect: syntax error\n", 0);
Tcl_AppendResult(interp, "pg_connect -conninfo <conninfo-string>", 0);
return TCL_ERROR;
} }
conn = PQconnectdb(argv[2]);
if (!strcmp("-conninfo", argv[1])) } else {
{ /*
* Establish a connection using the old PQsetdb() interface
/* */
* Establish a connection using the new PQconnectdb() interface if (argc > 2) {
*/ /* parse for pg environment settings */
if (argc != 3) i = 2;
{ while (i+1 < argc) {
Tcl_AppendResult(interp, "pg_connect: syntax error\n", 0); if (strcmp(argv[i], "-host") == 0) {
Tcl_AppendResult(interp, "pg_connect -conninfo <conninfo-string>", 0); pghost = argv[i+1];
return TCL_ERROR; i += 2;
} }
conn = PQconnectdb(argv[2]); else
} if (strcmp(argv[i], "-port") == 0) {
else pgport = argv[i+1];
{ i += 2;
}
/* else
* Establish a connection using the old PQsetdb() interface if (strcmp(argv[i], "-tty") == 0) {
*/ pgtty = argv[i+1];
if (argc > 2) i += 2;
{
/* parse for pg environment settings */
i = 2;
while (i + 1 < argc)
{
if (strcmp(argv[i], "-host") == 0)
{
pghost = argv[i + 1];
i += 2;
}
else if (strcmp(argv[i], "-port") == 0)
{
pgport = argv[i + 1];
i += 2;
}
else if (strcmp(argv[i], "-tty") == 0)
{
pgtty = argv[i + 1];
i += 2;
}
else if (strcmp(argv[i], "-options") == 0)
{
pgoptions = argv[i + 1];
i += 2;
}
else
{
Tcl_AppendResult(interp, "Bad option to pg_connect : \n",
argv[i], 0);
Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]", 0);
return TCL_ERROR;
}
} /* while */
if ((i % 2 != 0) || i != argc)
{
Tcl_AppendResult(interp, "wrong # of arguments to pg_connect\n", argv[i], 0);
Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]", 0);
return TCL_ERROR;
} }
} else if (strcmp(argv[i], "-options") == 0) {
dbName = argv[1]; pgoptions = argv[i+1];
conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); i += 2;
} }
else {
if (conn->status == CONNECTION_OK) Tcl_AppendResult(interp, "Bad option to pg_connect : \n",
{ argv[i], 0);
PgSetConnectionId(cd, interp->result, conn); Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]",0);
return TCL_OK; return TCL_ERROR;
} }
else } /* while */
{ if ((i % 2 != 0) || i != argc) {
Tcl_AppendResult(interp, "Connection to database failed\n", 0); Tcl_AppendResult(interp, "wrong # of arguments to pg_connect\n", argv[i],0);
Tcl_AppendResult(interp, conn->errorMessage, 0); Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]",0);
PQfinish(conn);
return TCL_ERROR; return TCL_ERROR;
}
} }
dbName = argv[1];
conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
}
if (conn->status == CONNECTION_OK) {
PgSetConnectionId(interp, conn);
return TCL_OK;
}
else {
Tcl_AppendResult(interp, "Connection to database failed\n", 0);
Tcl_AppendResult(interp, conn->errorMessage, 0);
PQfinish(conn);
return TCL_ERROR;
}
} }
/********************************** /**********************************
* pg_disconnect * pg_disconnect
close a backend connection close a backend connection
syntax: syntax:
pg_disconnect connection pg_disconnect connection
The argument passed in must be a connection pointer. The argument passed in must be a connection pointer.
**********************************/ **********************************/
int int
Pg_disconnect(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_disconnect(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; Tcl_Channel conn_chan;
PGconn *conn;
if (argc != 2) if (argc != 2) {
{ Tcl_AppendResult(interp, "Wrong # of arguments\n", "pg_disconnect connection", 0);
Tcl_AppendResult(interp, "Wrong # of arguments\n", "pg_disconnect connection", 0); return TCL_ERROR;
return TCL_ERROR; }
}
conn = PgGetConnectionId(cd, argv[1]); conn_chan = Tcl_GetChannel(interp, argv[1], 0);
if (conn == (PGconn *) NULL) if (conn_chan == NULL) {
{ Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); Tcl_AppendResult(interp, argv[1], " is not a valid connection\n", 0);
return TCL_ERROR; return TCL_ERROR;
} }
PgDelConnectionId(cd, argv[1]); return Tcl_UnregisterChannel(interp, conn_chan);
PQfinish(conn);
return TCL_OK;
} }
/********************************** /**********************************
* pg_exec * pg_exec
send a query string to the backend connection send a query string to the backend connection
syntax: syntax:
pg_exec connection query pg_exec connection query
the return result is either an error message or a handle for a query the return result is either an error message or a handle for a query
result. Handles start with the prefix "pgp" result. Handles start with the prefix "pgp"
**********************************/ **********************************/
int int
Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_exec(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; Pg_ConnectionId *connid;
PGconn *conn; PGconn *conn;
PGresult *result; PGresult *result;
int connStatus;
if (argc != 3) {
Tcl_AppendResult(interp, "Wrong # of arguments\n",
"pg_exec connection queryString", 0);
return TCL_ERROR;
}
if (argc != 3) conn = PgGetConnectionId(interp, argv[1], &connid);
{ if (conn == (PGconn *)NULL) {
Tcl_AppendResult(interp, "Wrong # of arguments\n", Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
"pg_exec connection queryString", 0); return TCL_ERROR;
return TCL_ERROR; }
}
conn = PgGetConnectionId(cd, argv[1]); if (connid->res_copyStatus != RES_COPY_NONE) {
if (conn == (PGconn *) NULL) Tcl_SetResult(interp, "Attempt to query while COPY in progress", TCL_STATIC);
{ return TCL_ERROR;
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); }
return TCL_ERROR;
}
result = PQexec(conn, argv[2]); connStatus = conn->status;
if (result) result = PQexec(conn, argv[2]);
{ if (result) {
PgSetResultId(cd, interp->result, argv[1], result); int rId = PgSetResultId(interp, argv[1], result);
return TCL_OK; if (result->resultStatus == PGRES_COPY_IN ||
result->resultStatus == PGRES_COPY_OUT) {
connid->res_copyStatus = RES_COPY_INPROGRESS;
connid->res_copy = rId;
} }
else
{
/* error occurred during the query */
Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC);
return TCL_ERROR;
}
/* check return status of result */
return TCL_OK; return TCL_OK;
}
else {
/* error occurred during the query */
Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC);
if (connStatus == CONNECTION_OK) {
PQreset(conn);
if (conn->status == CONNECTION_OK) {
result = PQexec(conn, argv[2]);
if (result) {
int rId = PgSetResultId(interp, argv[1], result);
if (result->resultStatus == PGRES_COPY_IN ||
result->resultStatus == PGRES_COPY_OUT) {
connid->res_copyStatus = RES_COPY_INPROGRESS;
connid->res_copy = rId;
}
return TCL_OK;
}
}
}
return TCL_ERROR;
}
} }
/********************************** /**********************************
* pg_result * pg_result
get information about the results of a query get information about the results of a query
syntax: syntax:
pg_result result ?option? pg_result result ?option?
the options are: the options are:
-status -status
the status of the result the status of the result
-conn -conn
the connection that produced the result the connection that produced the result
-assign arrayName -assign arrayName
assign the results to an array assign the results to an array
-assignbyidx arrayName ?appendstr? -assignbyidx arrayName
assign the results to an array using the first field as a key assign the results to an array using the first field as a key
optional appendstr append that string to the key name. Usefull for
creating pseudo-multi dimentional arrays in tcl.
-numTuples -numTuples
the number of tuples in the query the number of tuples in the query
-attributes -attributes
...@@ -502,333 +499,303 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) ...@@ -502,333 +499,303 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[])
returns the number of attributes returned by the query returns the number of attributes returned by the query
-getTuple tupleNumber -getTuple tupleNumber
returns the values of the tuple in a list returns the values of the tuple in a list
-clear -tupleArray tupleNumber arrayName
stores the values of the tuple in array arrayName, indexed
by the attributes returned
-clear
clear the result buffer. Do not reuse after this clear the result buffer. Do not reuse after this
**********************************/ **********************************/
int int
Pg_result(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_result(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGresult *result;
PGresult *result; char *opt;
char *opt; int i;
int i; int tupno;
int tupno; char prearrayInd[MAX_MESSAGE_LEN];
char prearrayInd[MAX_MESSAGE_LEN]; char arrayInd[MAX_MESSAGE_LEN];
char arrayInd[MAX_MESSAGE_LEN]; char *arrVar;
char *appendstr;
char *arrVar; if (argc < 3 || argc > 5) {
Tcl_AppendResult(interp, "Wrong # of arguments\n",0);
if (argc != 3 && argc != 4 && argc != 5) goto Pg_result_errReturn;
{ }
Tcl_AppendResult(interp, "Wrong # of arguments\n", 0);
goto Pg_result_errReturn; result = PgGetResultId(interp, argv[1]);
} if (result == (PGresult *)NULL) {
Tcl_AppendResult(interp, "First argument is not a valid query result", 0);
result = PgGetResultId(cd, argv[1]); return TCL_ERROR;
if (result == (PGresult *) NULL) }
{
Tcl_AppendResult(interp, "First argument is not a valid query result\n", 0);
return TCL_ERROR;
}
opt = argv[2]; opt = argv[2];
if (strcmp(opt, "-status") == 0) if (strcmp(opt, "-status") == 0) {
{ Tcl_AppendResult(interp, pgresStatus[PQresultStatus(result)], 0);
Tcl_AppendResult(interp, pgresStatus[PQresultStatus(result)], 0); return TCL_OK;
return TCL_OK; }
} else if (strcmp(opt, "-oid") == 0) {
else if (strcmp(opt, "-oid") == 0) Tcl_AppendResult(interp, PQoidStatus(result), 0);
{ return TCL_OK;
Tcl_AppendResult(interp, PQoidStatus(result), 0); }
return TCL_OK; else if (strcmp(opt, "-conn") == 0) {
} return PgGetConnByResultId(interp, argv[1]);
else if (strcmp(opt, "-conn") == 0) }
{ else if (strcmp(opt, "-clear") == 0) {
PgGetConnByResultId(cd, interp->result, argv[1]); PgDelResultId(interp, argv[1]);
return TCL_OK; PQclear(result);
} return TCL_OK;
else if (strcmp(opt, "-clear") == 0) }
{ else if (strcmp(opt, "-numTuples") == 0) {
PgDelResultId(cd, argv[1]); sprintf(interp->result, "%d", PQntuples(result));
PQclear(result); return TCL_OK;
return TCL_OK; }
} else if (strcmp(opt, "-assign") == 0) {
else if (strcmp(opt, "-numTuples") == 0) if (argc != 4) {
{ Tcl_AppendResult(interp, "-assign option must be followed by a variable name",0);
sprintf(interp->result, "%d", PQntuples(result)); return TCL_ERROR;
return TCL_OK; }
} arrVar = argv[3];
else if (strcmp(opt, "-assign") == 0) /* this assignment assigns the table of result tuples into a giant
{ array with the name given in the argument,
if (argc != 4) the indices of the array or (tupno,attrName)*/
{ for (tupno = 0; tupno<PQntuples(result); tupno++) {
Tcl_AppendResult(interp, "-assign option must be followed by a variable name", 0); for (i=0;i<PQnfields(result);i++) {
return TCL_ERROR; sprintf(arrayInd, "%d,%s", tupno, PQfname(result,i));
} Tcl_SetVar2(interp, arrVar, arrayInd,
arrVar = argv[3];
/*
* this assignment assigns the table of result tuples into a giant
* array with the name given in the argument, the indices of the
* array or (tupno,attrName)
*/
for (tupno = 0; tupno < PQntuples(result); tupno++)
{
for (i = 0; i < PQnfields(result); i++)
{
sprintf(arrayInd, "%d,%s", tupno, PQfname(result, i));
Tcl_SetVar2(interp, arrVar, arrayInd,
#ifdef TCL_ARRAYS #ifdef TCL_ARRAYS
tcl_value(PQgetvalue(result, tupno, i)), tcl_value(PQgetvalue(result,tupno,i)),
#else #else
PQgetvalue(result, tupno, i), PQgetvalue(result,tupno,i),
#endif #endif
TCL_LEAVE_ERR_MSG); TCL_LEAVE_ERR_MSG);
} }
}
Tcl_AppendResult(interp, arrVar, 0);
return TCL_OK;
} }
else if (strcmp(opt, "-assignbyidx") == 0) Tcl_AppendResult(interp, arrVar, 0);
{ return TCL_OK;
if (argc != 4 && argc != 5) }
{ else if (strcmp(opt, "-assignbyidx") == 0) {
Tcl_AppendResult(interp, "-assignbyidx requires the array name and takes one optional argument as an append string", 0); if (argc != 4) {
return TCL_ERROR; Tcl_AppendResult(interp, "-assignbyidx option must be followed by a variable name",0);
} return TCL_ERROR;
arrVar = argv[3]; }
arrVar = argv[3];
/* /* this assignment assigns the table of result tuples into a giant
* this assignment assigns the table of result tuples into a giant array with the name given in the argument,
* array with the name given in the argument, the indices of the the indices of the array or (tupno,attrName)*/
* array or (tupno,attrName) for (tupno = 0; tupno<PQntuples(result); tupno++) {
*/ sprintf(prearrayInd,"%s",PQgetvalue(result,tupno,0));
if (argc == 5) for (i=1;i<PQnfields(result);i++) {
{ sprintf(arrayInd, "%s,%s", prearrayInd, PQfname(result,i));
appendstr = argv[4]; Tcl_SetVar2(interp, arrVar, arrayInd,
} PQgetvalue(result,tupno,i),
else TCL_LEAVE_ERR_MSG);
{ }
appendstr = ""; }
} Tcl_AppendResult(interp, arrVar, 0);
for (tupno = 0; tupno < PQntuples(result); tupno++) return TCL_OK;
{ }
sprintf(prearrayInd, "%s", PQgetvalue(result, tupno, 0)); else if (strcmp(opt, "-getTuple") == 0) {
for (i = 1; i < PQnfields(result); i++) if (argc != 4) {
{ Tcl_AppendResult(interp, "-getTuple option must be followed by a tuple number",0);
sprintf(arrayInd, "%s,%s%s", prearrayInd, PQfname(result, i), return TCL_ERROR;
appendstr); }
Tcl_SetVar2(interp, arrVar, arrayInd, tupno = atoi(argv[3]);
PQgetvalue(result, tupno, i),
TCL_LEAVE_ERR_MSG); if (tupno >= PQntuples(result)) {
} Tcl_AppendResult(interp, "argument to getTuple cannot exceed number of tuples - 1",0);
} return TCL_ERROR;
Tcl_AppendResult(interp, arrVar, 0);
return TCL_OK;
} }
else if (strcmp(opt, "-getTuple") == 0)
{
if (argc != 4)
{
Tcl_AppendResult(interp, "-getTuple option must be followed by a tuple number", 0);
return TCL_ERROR;
}
tupno = atoi(argv[3]);
if (tupno >= PQntuples(result))
{
Tcl_AppendResult(interp, "argument to getTuple cannot exceed number of tuples - 1", 0);
return TCL_ERROR;
}
#ifdef TCL_ARRAYS #ifdef TCL_ARRAYS
for (i = 0; i < PQnfields(result); i++) for (i=0; i<PQnfields(result); i++) {
{ Tcl_AppendElement(interp, tcl_value(PQgetvalue(result,tupno,i)));
Tcl_AppendElement(interp, PQgetvalue(result, tupno, i)); }
}
#else #else
/* Tcl_AppendResult(interp, PQgetvalue(result,tupno,0),NULL); */ /* Tcl_AppendResult(interp, PQgetvalue(result,tupno,0),NULL); */
Tcl_AppendElement(interp, PQgetvalue(result, tupno, 0)); Tcl_AppendElement(interp, PQgetvalue(result,tupno,0));
for (i = 1; i < PQnfields(result); i++) for (i=1;i<PQnfields(result);i++) {
{ /* Tcl_AppendResult(interp, " ", PQgetvalue(result,tupno,i),NULL);*/
/* Tcl_AppendResult(interp, " ", PQgetvalue(result,tupno,i),NULL);*/ Tcl_AppendElement(interp, PQgetvalue(result,tupno,i));
Tcl_AppendElement(interp, PQgetvalue(result, tupno, i)); }
}
#endif #endif
return TCL_OK; return TCL_OK;
}
else if (strcmp(opt, "-tupleArray") == 0) {
if (argc != 5) {
Tcl_AppendResult(interp, "-tupleArray option must be followed by a tuple number and array name",0);
return TCL_ERROR;
} }
else if (strcmp(opt, "-attributes") == 0) tupno = atoi(argv[3]);
{
Tcl_AppendResult(interp, PQfname(result, 0), NULL); if (tupno >= PQntuples(result)) {
for (i = 1; i < PQnfields(result); i++) Tcl_AppendResult(interp, "argument to tupleArray cannot exceed number of tuples - 1",0);
{ return TCL_ERROR;
Tcl_AppendResult(interp, " ", PQfname(result, i), NULL);
}
return TCL_OK;
} }
else if (strcmp(opt, "-lAttributes") == 0)
{
char buf[512];
Tcl_ResetResult(interp); for ( i = 0; i < PQnfields(result); i++) {
for (i = 0; i < PQnfields(result); i++) if (Tcl_SetVar2(interp, argv[4], PQfname(result, i), PQgetvalue(result, tupno, i), TCL_LEAVE_ERR_MSG) == NULL) {
{ return TCL_ERROR;
sprintf(buf, "{%s} %ld %d", PQfname(result, i), }
(long) PQftype(result, i),
PQfsize(result, i));
Tcl_AppendElement(interp, buf);
}
return TCL_OK;
}
else if (strcmp(opt, "-numAttrs") == 0)
{
sprintf(interp->result, "%d", PQnfields(result));
return TCL_OK;
} }
else return TCL_OK;
{ }
Tcl_AppendResult(interp, "Invalid option", 0); else if (strcmp(opt, "-attributes") == 0) {
goto Pg_result_errReturn; Tcl_AppendResult(interp, PQfname(result,0),NULL);
} for (i=1;i<PQnfields(result);i++) {
Tcl_AppendResult(interp, " ", PQfname(result,i), NULL);
}
Pg_result_errReturn: return TCL_OK;
Tcl_AppendResult(interp, }
"pg_result result ?option? where ?option is\n", else if (strcmp(opt, "-lAttributes") == 0) {
"\t-status\n", char buf[512];
"\t-conn\n", Tcl_ResetResult(interp);
"\t-assign arrayVarName\n", for (i = 0; i < PQnfields(result); i++) {
"\t-assignbyidx arrayVarName ?appendstr?\n", sprintf(buf, "{%s} %ld %d", PQfname(result, i),
"\t-numTuples\n", (long) PQftype(result, i),
"\t-attributes\n" PQfsize(result, i));
"\t-lAttributes\n" Tcl_AppendElement(interp, buf);
"\t-numAttrs\n" }
"\t-getTuple tupleNumber\n", return TCL_OK;
"\t-clear\n", }
"\t-oid\n", else if (strcmp(opt, "-numAttrs") == 0) {
0); sprintf(interp->result, "%d", PQnfields(result));
return TCL_ERROR; return TCL_OK;
}
else {
Tcl_AppendResult(interp, "Invalid option",0);
goto Pg_result_errReturn;
}
Pg_result_errReturn:
Tcl_AppendResult(interp,
"pg_result result ?option? where ?option is\n",
"\t-status\n",
"\t-conn\n",
"\t-assign arrayVarName\n",
"\t-assignbyidx arrayVarName\n",
"\t-numTuples\n",
"\t-attributes\n"
"\t-lAttributes\n"
"\t-numAttrs\n"
"\t-getTuple tupleNumber\n",
"\t-tupleArray tupleNumber arrayVarName\n",
"\t-clear\n",
"\t-oid\n",
(char*)0);
return TCL_ERROR;
} }
/********************************** /**********************************
* pg_lo_open * pg_lo_open
open a large object open a large object
syntax: syntax:
pg_lo_open conn objOid mode pg_lo_open conn objOid mode
where mode can be either 'r', 'w', or 'rw' where mode can be either 'r', 'w', or 'rw'
**********************/ **********************/
int int
Pg_lo_open(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_lo_open(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGconn *conn;
PGconn *conn; int lobjId;
int lobjId; int mode;
int mode; int fd;
int fd;
if (argc != 4) {
if (argc != 4) Tcl_AppendResult(interp, "Wrong # of arguments\n",
{ "pg_lo_open connection lobjOid mode", 0);
Tcl_AppendResult(interp, "Wrong # of arguments\n", return TCL_ERROR;
"pg_lo_open connection lobjOid mode", 0); }
return TCL_ERROR;
} conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL);
if (conn == (PGconn *)NULL) {
conn = PgGetConnectionId(cd, argv[1]); Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
if (conn == (PGconn *) NULL) return TCL_ERROR;
{ }
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
return TCL_ERROR; lobjId = atoi(argv[2]);
} if (strlen(argv[3]) < 1 ||
strlen(argv[3]) > 2)
lobjId = atoi(argv[2]); {
if (strlen(argv[3]) < 1 || Tcl_AppendResult(interp,"mode argument must be 'r', 'w', or 'rw'",0);
strlen(argv[3]) > 2) return TCL_ERROR;
{ }
Tcl_AppendResult(interp, "mode argument must be 'r', 'w', or 'rw'", 0); switch (argv[3][0]) {
return TCL_ERROR; case 'r':
} case 'R':
switch (argv[3][0]) mode = INV_READ;
{ break;
case 'r': case 'w':
case 'R': case 'W':
mode = INV_READ; mode = INV_WRITE;
break; break;
case 'w': default:
case 'W': Tcl_AppendResult(interp,"mode argument must be 'r', 'w', or 'rw'",0);
mode = INV_WRITE; return TCL_ERROR;
break; }
default: switch (argv[3][1]) {
Tcl_AppendResult(interp, "mode argument must be 'r', 'w', or 'rw'", 0); case '\0':
return TCL_ERROR; break;
} case 'r':
switch (argv[3][1]) case 'R':
{ mode = mode & INV_READ;
case '\0': break;
break; case 'w':
case 'r': case 'W':
case 'R': mode = mode & INV_WRITE;
mode = mode & INV_READ; break;
break; default:
case 'w': Tcl_AppendResult(interp,"mode argument must be 'r', 'w', or 'rw'",0);
case 'W': return TCL_ERROR;
mode = mode & INV_WRITE; }
break;
default: fd = lo_open(conn,lobjId,mode);
Tcl_AppendResult(interp, "mode argument must be 'r', 'w', or 'rw'", 0); sprintf(interp->result,"%d",fd);
return TCL_ERROR; return TCL_OK;
}
fd = lo_open(conn, lobjId, mode);
sprintf(interp->result, "%d", fd);
return TCL_OK;
} }
/********************************** /**********************************
* pg_lo_close * pg_lo_close
close a large object close a large object
syntax: syntax:
pg_lo_close conn fd pg_lo_close conn fd
**********************/ **********************/
int int
Pg_lo_close(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_lo_close(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGconn *conn;
PGconn *conn; int fd;
int fd;
if (argc != 3) if (argc != 3) {
{ Tcl_AppendResult(interp, "Wrong # of arguments\n",
Tcl_AppendResult(interp, "Wrong # of arguments\n", "pg_lo_close connection fd", 0);
"pg_lo_close connection fd", 0); return TCL_ERROR;
return TCL_ERROR; }
}
conn = PgGetConnectionId(cd, argv[1]);
if (conn == (PGconn *) NULL)
{
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
return TCL_ERROR;
}
fd = atoi(argv[2]); conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL);
sprintf(interp->result, "%d", lo_close(conn, fd)); if (conn == (PGconn *)NULL) {
return TCL_OK; Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
return TCL_ERROR;
}
fd = atoi(argv[2]);
sprintf(interp->result,"%d",lo_close(conn,fd));
return TCL_OK;
} }
/********************************** /**********************************
* pg_lo_read * pg_lo_read
reads at most len bytes from a large object into a variable named reads at most len bytes from a large object into a variable named
bufVar bufVar
syntax: syntax:
pg_lo_read conn fd bufVar len pg_lo_read conn fd bufVar len
...@@ -836,104 +803,96 @@ Pg_lo_close(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) ...@@ -836,104 +803,96 @@ Pg_lo_close(ClientData cData, Tcl_Interp * interp, int argc, char *argv[])
**********************/ **********************/
int int
Pg_lo_read(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_lo_read(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGconn *conn;
PGconn *conn; int fd;
int fd; int nbytes = 0;
int nbytes = 0; char *buf;
char *buf; char *bufVar;
char *bufVar; int len;
int len;
if (argc != 5) {
if (argc != 5) Tcl_AppendResult(interp, "Wrong # of arguments\n",
{ " pg_lo_read conn fd bufVar len", 0);
Tcl_AppendResult(interp, "Wrong # of arguments\n", return TCL_ERROR;
" pg_lo_read conn fd bufVar len", 0); }
return TCL_ERROR;
}
conn = PgGetConnectionId(cd, argv[1]);
if (conn == (PGconn *) NULL)
{
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
return TCL_ERROR;
}
fd = atoi(argv[2]);
bufVar = argv[3];
len = atoi(argv[4]); 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;
}
fd = atoi(argv[2]);
if (len <= 0) bufVar = argv[3];
{
sprintf(interp->result, "%d", nbytes);
return TCL_OK;
}
buf = malloc(sizeof(len + 1));
nbytes = lo_read(conn, fd, buf, len); len = atoi(argv[4]);
Tcl_SetVar(interp, bufVar, buf, TCL_LEAVE_ERR_MSG); if (len <= 0) {
sprintf(interp->result, "%d", nbytes); sprintf(interp->result,"%d",nbytes);
free(buf);
return TCL_OK; return TCL_OK;
}
buf = ckalloc(sizeof(len+1));
nbytes = lo_read(conn,fd,buf,len);
Tcl_SetVar(interp,bufVar,buf,TCL_LEAVE_ERR_MSG);
sprintf(interp->result,"%d",nbytes);
ckfree(buf);
return TCL_OK;
} }
/*********************************** /***********************************
Pg_lo_write Pg_lo_write
write at most len bytes to a large object write at most len bytes to a large object
syntax: syntax:
pg_lo_write conn fd buf len pg_lo_write conn fd buf len
***********************************/ ***********************************/
int int
Pg_lo_write(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_lo_write(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGconn *conn;
PGconn *conn; char *buf;
char *buf; int fd;
int fd; int nbytes = 0;
int nbytes = 0; int len;
int len;
if (argc != 5) {
if (argc != 5) Tcl_AppendResult(interp, "Wrong # of arguments\n",
{ "pg_lo_write conn fd buf len", 0);
Tcl_AppendResult(interp, "Wrong # of arguments\n", return TCL_ERROR;
"pg_lo_write conn fd buf len", 0); }
return TCL_ERROR;
}
conn = PgGetConnectionId(cd, argv[1]);
if (conn == (PGconn *) NULL)
{
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
return TCL_ERROR;
}
fd = atoi(argv[2]); 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;
}
fd = atoi(argv[2]);
buf = argv[3]; buf = argv[3];
len = atoi(argv[4]); len = atoi(argv[4]);
if (len <= 0) if (len <= 0) {
{ sprintf(interp->result,"%d",nbytes);
sprintf(interp->result, "%d", nbytes);
return TCL_OK;
}
nbytes = lo_write(conn, fd, buf, len);
sprintf(interp->result, "%d", nbytes);
return TCL_OK; return TCL_OK;
}
nbytes = lo_write(conn,fd,buf,len);
sprintf(interp->result,"%d",nbytes);
return TCL_OK;
} }
/*********************************** /***********************************
Pg_lo_lseek Pg_lo_lseek
seek to a certain position in a large object seek to a certain position in a large object
syntax syntax
pg_lo_lseek conn fd offset whence pg_lo_lseek conn fd offset whence
...@@ -942,54 +901,43 @@ whence can be either ...@@ -942,54 +901,43 @@ whence can be either
"SEEK_CUR", "SEEK_END", or "SEEK_SET" "SEEK_CUR", "SEEK_END", or "SEEK_SET"
***********************************/ ***********************************/
int int
Pg_lo_lseek(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_lo_lseek(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGconn *conn;
PGconn *conn; int fd;
int fd; char *whenceStr;
char *whenceStr; int offset, whence;
int offset,
whence; if (argc != 5) {
Tcl_AppendResult(interp, "Wrong # of arguments\n",
if (argc != 5) "pg_lo_lseek conn fd offset whence", 0);
{ return TCL_ERROR;
Tcl_AppendResult(interp, "Wrong # of arguments\n", }
"pg_lo_lseek conn fd offset whence", 0);
return TCL_ERROR;
}
conn = PgGetConnectionId(cd, argv[1]);
if (conn == (PGconn *) NULL)
{
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
return TCL_ERROR;
}
fd = atoi(argv[2]);
offset = atoi(argv[3]);
whenceStr = argv[4];
if (strcmp(whenceStr, "SEEK_SET") == 0)
{
whence = SEEK_SET;
}
else if (strcmp(whenceStr, "SEEK_CUR") == 0)
{
whence = SEEK_CUR;
}
else if (strcmp(whenceStr, "SEEK_END") == 0)
{
whence = SEEK_END;
}
else
{
Tcl_AppendResult(interp, "the whence argument to Pg_lo_lseek must be SEEK_SET, SEEK_CUR or SEEK_END", 0);
return TCL_ERROR;
}
sprintf(interp->result, "%d", lo_lseek(conn, fd, offset, whence)); conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL);
return TCL_OK; if (conn == (PGconn *)NULL) {
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
return TCL_ERROR;
}
fd = atoi(argv[2]);
offset = atoi(argv[3]);
whenceStr = argv[4];
if (strcmp(whenceStr,"SEEK_SET") == 0) {
whence = SEEK_SET;
} else if (strcmp(whenceStr,"SEEK_CUR") == 0) {
whence = SEEK_CUR;
} else if (strcmp(whenceStr,"SEEK_END") == 0) {
whence = SEEK_END;
} else {
Tcl_AppendResult(interp, "the whence argument to Pg_lo_lseek must be SEEK_SET, SEEK_CUR or SEEK_END",0);
return TCL_ERROR;
}
sprintf(interp->result,"%d",lo_lseek(conn,fd,offset,whence));
return TCL_OK;
} }
...@@ -1000,113 +948,96 @@ Pg_lo_creat ...@@ -1000,113 +948,96 @@ Pg_lo_creat
syntax: syntax:
pg_lo_creat conn mode pg_lo_creat conn mode
mode can be any OR'ing together of INV_READ, INV_WRITE mode can be any OR'ing together of INV_READ, INV_WRITE,
for now, we don't support any additional storage managers. for now, we don't support any additional storage managers.
***********************************/ ***********************************/
int int
Pg_lo_creat(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_lo_creat(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGconn *conn;
PGconn *conn; char *modeStr;
char *modeStr; char *modeWord;
char *modeWord; int mode;
int mode;
if (argc != 3) {
if (argc != 3) Tcl_AppendResult(interp, "Wrong # of arguments\n",
{ "pg_lo_creat conn mode", 0);
Tcl_AppendResult(interp, "Wrong # of arguments\n", return TCL_ERROR;
"pg_lo_creat conn mode", 0); }
return TCL_ERROR;
}
conn = PgGetConnectionId(cd, argv[1]);
if (conn == (PGconn *) NULL)
{
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
return TCL_ERROR;
}
modeStr = argv[2];
modeWord = strtok(modeStr, "|");
if (strcmp(modeWord, "INV_READ") == 0)
{
mode = INV_READ;
}
else if (strcmp(modeWord, "INV_WRITE") == 0)
{
mode = INV_WRITE;
}
else
{
Tcl_AppendResult(interp,
"invalid mode argument to Pg_lo_creat\nmode argument must be some OR'd combination of INV_READ, and INV_WRITE",
0);
return TCL_ERROR;
}
while ((modeWord = strtok((char *) NULL, "|")) != NULL) conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL);
{ if (conn == (PGconn *)NULL) {
if (strcmp(modeWord, "INV_READ") == 0) Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
{ return TCL_ERROR;
mode |= INV_READ; }
}
else if (strcmp(modeWord, "INV_WRITE") == 0) modeStr = argv[2];
{
mode |= INV_WRITE; modeWord = strtok(modeStr,"|");
} if (strcmp(modeWord,"INV_READ") == 0) {
else mode = INV_READ;
{ } else if (strcmp(modeWord,"INV_WRITE") == 0) {
Tcl_AppendResult(interp, mode = INV_WRITE;
"invalid mode argument to Pg_lo_creat\nmode argument must be some OR'd combination of INV_READ, and INV_WRITE", } else {
0); Tcl_AppendResult(interp,
return TCL_ERROR; "invalid mode argument to Pg_lo_creat\nmode argument must be some OR'd combination of INV_READ, and INV_WRITE",
} 0);
} return TCL_ERROR;
sprintf(interp->result, "%d", lo_creat(conn, mode)); }
return TCL_OK;
while ( (modeWord = strtok((char*)NULL, "|")) != NULL) {
if (strcmp(modeWord,"INV_READ") == 0) {
mode |= INV_READ;
} else if (strcmp(modeWord,"INV_WRITE") == 0) {
mode |= INV_WRITE;
} else {
Tcl_AppendResult(interp,
"invalid mode argument to Pg_lo_creat\nmode argument must be some OR'd combination of INV_READ, INV_WRITE",
0);
return TCL_ERROR;
}
}
sprintf(interp->result,"%d",lo_creat(conn,mode));
return TCL_OK;
} }
/*********************************** /***********************************
Pg_lo_tell Pg_lo_tell
returns the current seek location of the large object returns the current seek location of the large object
syntax: syntax:
pg_lo_tell conn fd pg_lo_tell conn fd
***********************************/ ***********************************/
int int
Pg_lo_tell(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_lo_tell(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGconn *conn;
PGconn *conn; int fd;
int fd;
if (argc != 3)
{
Tcl_AppendResult(interp, "Wrong # of arguments\n",
"pg_lo_tell conn fd", 0);
return TCL_ERROR;
}
conn = PgGetConnectionId(cd, argv[1]); if (argc != 3) {
if (conn == (PGconn *) NULL) Tcl_AppendResult(interp, "Wrong # of arguments\n",
{ "pg_lo_tell conn fd", 0);
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR;
return TCL_ERROR; }
}
fd = atoi(argv[2]); 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;
}
fd = atoi(argv[2]);
sprintf(interp->result, "%d", lo_tell(conn, fd)); sprintf(interp->result,"%d",lo_tell(conn,fd));
return TCL_OK; return TCL_OK;
} }
/*********************************** /***********************************
Pg_lo_unlink Pg_lo_unlink
unlink a file based on lobject id unlink a file based on lobject id
syntax: syntax:
pg_lo_unlink conn lobjId pg_lo_unlink conn lobjId
...@@ -1114,43 +1045,39 @@ Pg_lo_unlink ...@@ -1114,43 +1045,39 @@ Pg_lo_unlink
***********************************/ ***********************************/
int int
Pg_lo_unlink(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_lo_unlink(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGconn *conn;
PGconn *conn; int lobjId;
int lobjId; int retval;
int retval;
if (argc != 3)
{
Tcl_AppendResult(interp, "Wrong # of arguments\n",
"pg_lo_tell conn fd", 0);
return TCL_ERROR;
}
conn = PgGetConnectionId(cd, argv[1]); if (argc != 3) {
if (conn == (PGconn *) NULL) Tcl_AppendResult(interp, "Wrong # of arguments\n",
{ "pg_lo_tell conn fd", 0);
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR;
return TCL_ERROR; }
}
lobjId = atoi(argv[2]);
retval = lo_unlink(conn, lobjId); conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL);
if (retval == -1) if (conn == (PGconn *)NULL) {
{ Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
sprintf(interp->result, "Pg_lo_unlink of '%d' failed", lobjId); return TCL_ERROR;
return TCL_ERROR; }
}
lobjId = atoi(argv[2]);
sprintf(interp->result, "%d", retval); retval = lo_unlink(conn,lobjId);
return TCL_OK; if (retval == -1) {
sprintf(interp->result,"Pg_lo_unlink of '%d' failed",lobjId);
return TCL_ERROR;
}
sprintf(interp->result,"%d",retval);
return TCL_OK;
} }
/*********************************** /***********************************
Pg_lo_import Pg_lo_import
import a Unix file into an (inversion) large objct import a Unix file into an (inversion) large objct
returns the oid of that object upon success returns the oid of that object upon success
returns InvalidOid upon failure returns InvalidOid upon failure
...@@ -1160,87 +1087,79 @@ Pg_lo_import ...@@ -1160,87 +1087,79 @@ Pg_lo_import
***********************************/ ***********************************/
int int
Pg_lo_import(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_lo_import(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGconn *conn;
PGconn *conn; char* filename;
char *filename; Oid lobjId;
Oid lobjId;
if (argc != 3)
{
Tcl_AppendResult(interp, "Wrong # of arguments\n",
"pg_lo_import conn filename", 0);
return TCL_ERROR;
}
conn = PgGetConnectionId(cd, argv[1]); if (argc != 3) {
if (conn == (PGconn *) NULL) Tcl_AppendResult(interp, "Wrong # of arguments\n",
{ "pg_lo_import conn filename", 0);
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); return TCL_ERROR;
return TCL_ERROR; }
}
filename = argv[2]; 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;
}
filename = argv[2];
lobjId = lo_import(conn, filename); lobjId = lo_import(conn,filename);
if (lobjId == InvalidOid) if (lobjId == InvalidOid) {
{ sprintf(interp->result, "Pg_lo_import of '%s' failed",filename);
sprintf(interp->result, "Pg_lo_import of '%s' failed", filename); return TCL_ERROR;
return TCL_ERROR; }
} sprintf(interp->result,"%d",lobjId);
sprintf(interp->result, "%d", lobjId); return TCL_OK;
return TCL_OK;
} }
/*********************************** /***********************************
Pg_lo_export Pg_lo_export
export an Inversion large object to a Unix file export an Inversion large object to a Unix file
syntax: syntax:
pg_lo_export conn lobjId filename pg_lo_export conn lobjId filename
***********************************/ ***********************************/
int int
Pg_lo_export(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_lo_export(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGconn *conn;
PGconn *conn; char* filename;
char *filename; Oid lobjId;
Oid lobjId; int retval;
int retval;
if (argc != 4) {
if (argc != 4) Tcl_AppendResult(interp, "Wrong # of arguments\n",
{ "pg_lo_export conn lobjId filename", 0);
Tcl_AppendResult(interp, "Wrong # of arguments\n", return TCL_ERROR;
"pg_lo_export conn lobjId filename", 0); }
return TCL_ERROR;
}
conn = PgGetConnectionId(cd, argv[1]);
if (conn == (PGconn *) NULL)
{
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
return TCL_ERROR;
}
lobjId = atoi(argv[2]);
filename = argv[3];
retval = lo_export(conn, lobjId, filename); conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL);
if (retval == -1) if (conn == (PGconn *)NULL) {
{ Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
sprintf(interp->result, "Pg_lo_export %d %s failed", lobjId, filename); return TCL_ERROR;
return TCL_ERROR; }
}
return TCL_OK; lobjId = atoi(argv[2]);
filename = argv[3];
retval = lo_export(conn,lobjId,filename);
if (retval == -1) {
sprintf(interp->result, "Pg_lo_export %d %s failed",lobjId, filename);
return TCL_ERROR;
}
return TCL_OK;
} }
/********************************** /**********************************
* pg_select * pg_select
send a select query string to the backend connection send a select query string to the backend connection
syntax: syntax:
pg_select connection query var proc pg_select connection query var proc
...@@ -1250,7 +1169,7 @@ Pg_lo_export(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) ...@@ -1250,7 +1169,7 @@ Pg_lo_export(ClientData cData, Tcl_Interp * interp, int argc, char *argv[])
Originally I was also going to update changes but that has turned out Originally I was also going to update changes but that has turned out
to be not so simple. Instead, the caller should get the OID of any to be not so simple. Instead, the caller should get the OID of any
table they want to update and update it themself in the loop. I may table they want to update and update it themself in the loop. I may
try to write a simplified table lookup and update function to make try to write a simplified table lookup and update function to make
that task a little easier. that task a little easier.
...@@ -1259,45 +1178,39 @@ Pg_lo_export(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) ...@@ -1259,45 +1178,39 @@ Pg_lo_export(ClientData cData, Tcl_Interp * interp, int argc, char *argv[])
**********************************/ **********************************/
int int
Pg_select(ClientData cData, Tcl_Interp * interp, int argc, char **argv) Pg_select(ClientData cData, Tcl_Interp *interp, int argc, char **argv)
{ {
Pg_clientData *cd = (Pg_clientData *) cData; PGconn *conn;
PGconn *conn; PGresult *result;
PGresult *result; int r;
int r; size_t tupno, column, ncols;
size_t tupno,
column,
ncols;
Tcl_DString headers; Tcl_DString headers;
char buffer[2048]; char buffer[2048];
struct struct info_s {
{ char *cname;
char *cname; int change;
int change; } *info;
} *info;
if (argc != 5) if (argc != 5)
{ {
Tcl_AppendResult(interp, "Wrong # of arguments\n", Tcl_AppendResult(interp, "Wrong # of arguments\n",
"pg_select connection queryString var proc", 0); "pg_select connection queryString var proc", 0);
return TCL_ERROR;
}
conn = PgGetConnectionId(cd, argv[1]);
if (conn == (PGconn *) NULL)
{
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
return TCL_ERROR; return TCL_ERROR;
} }
conn = PgGetConnectionId(interp, argv[1], (Pg_ConnectionId**)NULL);
if (conn == (PGconn *)NULL) {
return TCL_ERROR;
}
if ((result = PQexec(conn, argv[2])) == 0) if ((result = PQexec(conn, argv[2])) == 0)
{ {
/* error occurred during the query */ /* error occurred during the query */
Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC); Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC);
return TCL_ERROR; return TCL_ERROR;
} }
if ((info = malloc(sizeof(*info) * (ncols = PQnfields(result)))) == NULL) if ((info = (struct info_s *)ckalloc(sizeof(*info) * (ncols = PQnfields(result)))) == NULL)
{ {
Tcl_AppendResult(interp, "Not enough memory", 0); Tcl_AppendResult(interp, "Not enough memory", 0);
return TCL_ERROR; return TCL_ERROR;
...@@ -1323,8 +1236,8 @@ Pg_select(ClientData cData, Tcl_Interp * interp, int argc, char **argv) ...@@ -1323,8 +1236,8 @@ Pg_select(ClientData cData, Tcl_Interp * interp, int argc, char **argv)
for (column = 0; column < ncols; column++) for (column = 0; column < ncols; column++)
{ {
Tcl_SetVar2(interp, argv[3], info[column].cname, strcpy(buffer, PQgetvalue(result, tupno, column));
PQgetvalue(result, tupno, column), 0); Tcl_SetVar2(interp, argv[3], info[column].cname, buffer, 0);
} }
Tcl_SetVar2(interp, argv[3], ".command", "update", 0); Tcl_SetVar2(interp, argv[3], ".command", "update", 0);
...@@ -1332,196 +1245,166 @@ Pg_select(ClientData cData, Tcl_Interp * interp, int argc, char **argv) ...@@ -1332,196 +1245,166 @@ Pg_select(ClientData cData, Tcl_Interp * interp, int argc, char **argv)
if ((r = Tcl_Eval(interp, argv[4])) != TCL_OK && r != TCL_CONTINUE) if ((r = Tcl_Eval(interp, argv[4])) != TCL_OK && r != TCL_CONTINUE)
{ {
if (r == TCL_BREAK) if (r == TCL_BREAK)
{
/*
* I suppose that memory used by info and result must be
* released
*/
free(info);
PQclear(result);
Tcl_UnsetVar(interp, argv[3], 0);
return TCL_OK; return TCL_OK;
}
if (r == TCL_ERROR) if (r == TCL_ERROR)
{ {
char msg[60]; char msg[60];
sprintf(msg, "\n (\"pg_select\" body line %d)", sprintf(msg, "\n (\"pg_select\" body line %d)",
interp->errorLine); interp->errorLine);
Tcl_AddErrorInfo(interp, msg); Tcl_AddErrorInfo(interp, msg);
} }
/* also, releasing memory used by info and result */
free(info);
PQclear(result);
Tcl_UnsetVar(interp, argv[3], 0);
return r; return r;
} }
} }
free(info); ckfree((void*)info);
/* Release memory used by result */
PQclear(result);
Tcl_UnsetVar(interp, argv[3], 0); Tcl_UnsetVar(interp, argv[3], 0);
Tcl_AppendResult(interp, "", 0); Tcl_AppendResult(interp, "", 0);
return TCL_OK; return TCL_OK;
} }
int int
Pg_listen(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_listen(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; int new;
int new; char *relname;
char *relname; char *callback = NULL;
char *callback = NULL; Tcl_HashEntry *entry;
Tcl_HashEntry *entry; Pg_ConnectionId *connid;
PGconn *conn; PGconn *conn;
PGresult *result; PGresult *result;
if ((argc < 3) || (argc > 4)) if ((argc < 3) || (argc > 4)) {
{ Tcl_AppendResult(interp, "wrong # args, should be \"",
Tcl_AppendResult(interp, "wrong # args, should be \"", argv[0], " connection relname ?callback?\"", 0);
argv[0], " connection relname ?callback?\"", 0); return TCL_ERROR;
return TCL_ERROR; }
}
/*
/* * Get the command arguments. Note that relname will copied by
* Get the command arguments. Note that relname will copied by * Tcl_CreateHashEntry while callback must be allocated.
* Tcl_CreateHashEntry while callback must be allocated. */
*/ conn = PgGetConnectionId(interp, argv[1], &connid);
conn = (PGconn *) PgGetConnectionId(cd, argv[1]); if (conn == (PGconn *)NULL) {
if (conn == (PGconn *) NULL) Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
{ return TCL_ERROR;
Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0); }
return TCL_ERROR; relname = argv[2];
} if ((argc > 3) && *argv[3]) {
relname = argv[2]; callback = (char *) ckalloc((unsigned) (strlen(argv[3])+1));
if ((argc > 3) && *argv[3]) strcpy(callback, argv[3]);
{ }
callback = (char *) ckalloc((unsigned) (strlen(argv[3]) + 1));
strcpy(callback, argv[3]); /*
} * Set or update a callback for a relation;
*/
/* if (callback) {
* Set or update a callback for a relation; entry = Tcl_CreateHashEntry(&(connid->notify_hash), relname, &new);
*/ if (new) {
if (callback) /* New callback, execute a listen command on the relation */
{ char *cmd = (char *) ckalloc((unsigned) (strlen(argv[2])+8));
entry = Tcl_CreateHashEntry(&(cd->notify_hash), relname, &new); sprintf(cmd, "LISTEN %s", relname);
if (new) result = PQexec(conn, cmd);
{ ckfree(cmd);
/* New callback, execute a listen command on the relation */ if (!result || (result->resultStatus != PGRES_COMMAND_OK)) {
char *cmd = (char *) ckalloc((unsigned) (strlen(argv[2]) + 8)); /* Error occurred during the execution of command */
if (result) PQclear(result);
sprintf(cmd, "LISTEN %s", relname); ckfree(callback);
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);
}
else
{
/* Free the old callback string */
ckfree((char *) Tcl_GetHashValue(entry));
}
/* 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(&(cd->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); Tcl_DeleteHashEntry(entry);
} Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC);
return TCL_ERROR;
return TCL_OK; }
PQclear(result);
} else {
/* Free the old callback string */
ckfree((char *) Tcl_GetHashValue(entry));
}
/* 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);
}
return TCL_OK;
} }
int int
Pg_notifies(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_notifies(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
{ {
Pg_clientData *cd = (Pg_clientData *) cData; int count;
int count; char buff[12];
char buff[12]; char *callback;
char *callback; Tcl_HashEntry *entry;
Tcl_HashEntry *entry; Pg_ConnectionId *connid;
PGconn *conn; PGconn *conn;
PGresult *result; PGresult *result;
PGnotify *notify; PGnotify *notify;
if (argc != 2) if (argc != 2) {
{ Tcl_AppendResult(interp, "wrong # args, should be \"",
Tcl_AppendResult(interp, "wrong # args, should be \"", argv[0], " connection\"", 0);
argv[0], " connection\"", 0); return TCL_ERROR;
return TCL_ERROR; }
}
/*
/* * Get the connection argument.
* Get the connection argument. */
*/ conn = (PGconn*)PgGetConnectionId(interp, argv[1], &connid);
conn = (PGconn *) PgGetConnectionId(cd, argv[1]); if (conn == (PGconn *)NULL) {
if (conn == (PGconn *) NULL) Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
{ return TCL_ERROR;
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, " ");
/* Execute an empty command to retrieve asynchronous notifications */ if (result == NULL) {
result = PQexec(conn, " "); /* Error occurred during the execution of command */
if (result == NULL) Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC);
{ return TCL_ERROR;
/* Error occurred during the execution of command */ }
Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC); PQclear(result);
return TCL_ERROR;
} /*
PQclear(result); * Loop while there are pending notifies.
*/
/* for (count=0; count < 999; count++) {
* Loop while there are pending notifies. /* See if there is a pending notification */
*/ notify = PQnotifies(conn);
for (count = 0; count < 999; count++) if (notify == NULL) {
{ break;
/* See if there is a pending notification */ }
notify = PQnotifies(conn); entry = Tcl_FindHashEntry(&(connid->notify_hash), notify->relname);
if (notify == NULL) if (entry != NULL) {
{ callback = (char*)Tcl_GetHashValue(entry);
break; if (callback) {
} /* This should be a global eval, shouldn't it? */
entry = Tcl_FindHashEntry(&(cd->notify_hash), notify->relname); Tcl_Eval(interp, callback);
if (entry != NULL) /* And what if there's an error. Bgerror should be called? */
{ }
callback = Tcl_GetHashValue(entry); }
if (callback) free(notify);
{ }
Tcl_Eval(interp, callback);
} /*
} * Return the number of notifications processed.
free(notify); */
} sprintf(buff, "%d", count);
Tcl_SetResult(interp, buff, TCL_VOLATILE);
/* return TCL_OK;
* Return the number of notifications processed.
*/
sprintf(buff, "%d", count);
Tcl_SetResult(interp, buff, TCL_VOLATILE);
return TCL_OK;
} }
...@@ -5,7 +5,7 @@ ...@@ -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 @@ ...@@ -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;
Tcl_HashTable notify_hash;
long dbh_count;
long res_count;
} Pg_clientData;
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;
#define RES_COPY_NONE 0
typedef struct Pg_ResultId_s #define RES_COPY_INPROGRESS 1
{ #define RES_COPY_FIN 2
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 */
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* 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;
}
}
/*
* 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 the Id for a new connection and hash it * 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;
hent = Tcl_CreateHashEntry(&(cd->dbh_hash), connid->id, &hnew); connid->res_copy = -1;
Tcl_SetHashValue(hent, (ClientData) connid); 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);
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) ...@@ -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);
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) ...@@ -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;
connid = (Pg_ConnectionId *)cData;
hent = Tcl_FindHashEntry(&(cd->dbh_hash), id);
if (hent == NULL) for (i = 0; i < connid->res_max; i++) {
{ if (connid->results[i])
return; PQclear(connid->results[i]);
} }
ckfree((void*)connid->results);
connid = (Pg_ConnectionId *) Tcl_GetHashValue(hent);
for (entry = Tcl_FirstHashEntry(&(connid->notify_hash), &hsearch);
hent2 = Tcl_FirstHashEntry(&(connid->res_hash), &hsearch); entry != NULL;
while (hent2 != NULL) entry = Tcl_NextHashEntry(&hsearch))
{ {
resid = (Pg_ResultId *) Tcl_GetHashValue(hent2); hval = (char*)Tcl_GetHashValue(entry);
PQclear(resid->result); ckfree(hval);
hent3 = Tcl_FindHashEntry(&(cd->res_hash), resid->id); }
if (hent3 != NULL)
{ Tcl_DeleteHashTable(&connid->notify_hash);
Tcl_DeleteHashEntry(hent3); PQfinish(connid->conn);
} ckfree((void*)connid);
ckfree(resid); return 0;
hent2 = Tcl_NextHashEntry(&hsearch);
}
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)
{
connid = NULL;
}
else
{
connid = (Pg_ConnectionId *) Tcl_GetHashValue(hent);
}
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);
hent = Tcl_CreateHashEntry(&(cd->res_hash), resid->id, &hnew); conn_chan = Tcl_GetChannel(interp, connid_c, 0);
Tcl_SetHashValue(hent, (ClientData) resid); if(conn_chan == NULL)
return TCL_ERROR;
connid = (Pg_ConnectionId *)Tcl_GetChannelInstanceData(conn_chan);
if (connid != NULL) for (resid = connid->res_last+1; resid != connid->res_last; resid++) {
if (resid == connid->res_max)
resid = 0;
if (!connid->results[resid])
{ {
hent = Tcl_CreateHashEntry(&(connid->res_hash), resid->id, &hnew); connid->res_last = resid;
Tcl_SetHashValue(hent, (ClientData) resid); break;
}
}
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;
}
connid->results[resid] = res;
sprintf(buf, "%s.%d", connid_c, resid);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return 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 (!(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 * 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) ...@@ -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);
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); resid = getresid(interp, id, &connid);
ckfree(resid); if (resid == -1)
return;
connid->results[resid] = 0;
} }
/* /*
* 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;
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;
}
error_out:
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, resid_c, " is not a valid connection\n", 0);
return TCL_ERROR;
}
hent = Tcl_FindHashEntry(&(cd->res_hash), id);
if (hent == NULL)
{
return;
}
resid = (Pg_ResultId *) Tcl_GetHashValue(hent);
if (resid->connection != NULL)
{
strcpy(id, resid->connection->id);
}
}
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* 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;
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