📄 pgtclid.c
字号:
/*------------------------------------------------------------------------- * * pgtclId.c * * Contains Tcl "channel" interface routines, plus useful routines * to convert between strings and pointers. These are needed because * everything in Tcl is a string, but in C, pointers to data structures * are needed. * * ASSUMPTION: sizeof(long) >= sizeof(void*) * * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/interfaces/libpgtcl/pgtclId.c,v 1.20 1999/05/25 22:43:46 momjian Exp $ * *------------------------------------------------------------------------- */#include <stdlib.h>#include <string.h>#include <errno.h>#include "postgres.h"#include "pgtclCmds.h"#include "pgtclId.h"static intPgEndCopy(Pg_ConnectionId *connid, int *errorCodePtr){ connid->res_copyStatus = RES_COPY_NONE; if (PQendcopy(connid->conn)) { PQclear(connid->results[connid->res_copy]); connid->results[connid->res_copy] = PQmakeEmptyPGresult(connid->conn, PGRES_BAD_RESPONSE); connid->res_copy = -1; *errorCodePtr = EIO; return -1; } else { PQclear(connid->results[connid->res_copy]); connid->results[connid->res_copy] = PQmakeEmptyPGresult(connid->conn, PGRES_COMMAND_OK); connid->res_copy = -1; return 0; }}/* * Called when reading data (via gets) for a copy <rel> to stdout. */intPgInputProc(DRIVER_INPUT_PROTO){ Pg_ConnectionId *connid; PGconn *conn; int avail; connid = (Pg_ConnectionId *) cData; conn = connid->conn; if (connid->res_copy < 0 || PQresultStatus(connid->results[connid->res_copy]) != PGRES_COPY_OUT) { *errorCodePtr = EBUSY; return -1; } /* * Read any newly arrived data into libpq's buffer, thereby clearing * the socket's read-ready condition. */ if (!PQconsumeInput(conn)) { *errorCodePtr = EIO; return -1; } /* Move data from libpq's buffer to Tcl's. */ avail = PQgetlineAsync(conn, buf, bufSize); if (avail < 0) { /* Endmarker detected, change state and return 0 */ return PgEndCopy(connid, errorCodePtr); } return avail;}/* * Called when writing data (via puts) for a copy <rel> from stdin */intPgOutputProc(DRIVER_OUTPUT_PROTO){ Pg_ConnectionId *connid; PGconn *conn; connid = (Pg_ConnectionId *) cData; conn = connid->conn; if (connid->res_copy < 0 || PQresultStatus(connid->results[connid->res_copy]) != PGRES_COPY_IN) { *errorCodePtr = EBUSY; return -1; } if (PQputnbytes(conn, buf, bufSize)) { *errorCodePtr = EIO; return -1; } /* * This assumes Tcl script will write the terminator line in a single * operation; maybe not such a good assumption? */ if (bufSize >= 3 && strncmp(&buf[bufSize - 3], "\\.\n", 3) == 0) { if (PgEndCopy(connid, errorCodePtr) == -1) return -1; } return bufSize;}#if HAVE_TCL_GETFILEPROCTcl_FilePgGetFileProc(ClientData cData, int direction){ return (Tcl_File) NULL;}#endifTcl_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 */voidPgSetConnectionId(Tcl_Interp *interp, PGconn *conn){ Tcl_Channel conn_chan; Pg_ConnectionId *connid; int i; connid = (Pg_ConnectionId *) ckalloc(sizeof(Pg_ConnectionId)); connid->conn = conn; connid->res_count = 0; connid->res_last = -1; connid->res_max = RES_START; connid->res_hardmax = RES_HARD_MAX; connid->res_copy = -1; connid->res_copyStatus = RES_COPY_NONE; connid->results = (PGresult **) ckalloc(sizeof(PGresult *) * RES_START); for (i = 0; i < RES_START; i++) connid->results[i] = NULL; connid->notify_list = NULL; connid->notifier_running = 0; connid->notifier_socket = -1; sprintf(connid->id, "pgsql%d", PQsocket(conn));#if TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 5 /* Original signature (only seen in Tcl 7.5) */ conn_chan = Tcl_CreateChannel(&Pg_ConnType, connid->id, NULL, NULL, (ClientData) connid);#else /* Tcl 7.6 and later use this */ 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);}/* * Get back the connection from the Id */PGconn *PgGetConnectionId(Tcl_Interp *interp, char *id, Pg_ConnectionId **connid_p){ Tcl_Channel conn_chan; Pg_ConnectionId *connid; conn_chan = Tcl_GetChannel(interp, id, 0); if (conn_chan == NULL || Tcl_GetChannelType(conn_chan) != &Pg_ConnType) { Tcl_ResetResult(interp); Tcl_AppendResult(interp, id, " is not a valid postgresql connection", 0); return (PGconn *) NULL; } connid = (Pg_ConnectionId *) Tcl_GetChannelInstanceData(conn_chan); if (connid_p) *connid_p = connid; return connid->conn;}/* * Remove a connection Id from the hash table and * close all portals the user forgot. */intPgDelConnectionId(DRIVER_DEL_PROTO){ Tcl_HashEntry *entry; Tcl_HashSearch hsearch; Pg_ConnectionId *connid; Pg_TclNotifies *notifies; int i; connid = (Pg_ConnectionId *) cData; for (i = 0; i < connid->res_max; i++) { if (connid->results[i]) PQclear(connid->results[i]); } ckfree((void *) connid->results); /* Release associated notify info */ while ((notifies = connid->notify_list) != NULL) { connid->notify_list = notifies->next; for (entry = Tcl_FirstHashEntry(¬ifies->notify_hash, &hsearch); entry != NULL; entry = Tcl_NextHashEntry(&hsearch)) ckfree((char *) Tcl_GetHashValue(entry)); Tcl_DeleteHashTable(¬ifies->notify_hash); Tcl_DontCallWhenDeleted(notifies->interp, PgNotifyInterpDelete, (ClientData) notifies); ckfree((void *) notifies); } /* * Turn off the Tcl event source for this connection, and delete any * pending notify events. */ PgStopNotifyEventSource(connid); /* Close the libpq connection too */ PQfinish(connid->conn); connid->conn = NULL; /* * We must use Tcl_EventuallyFree because we don't want the connid * struct to vanish instantly if Pg_Notify_EventProc is active for it. * (Otherwise, closing the connection from inside a pg_listen callback * could lead to coredump.) Pg_Notify_EventProc can detect that the * connection has been deleted from under it by checking connid->conn. */ Tcl_EventuallyFree((ClientData) connid, TCL_DYNAMIC); return 0;}/* * 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. */intPgSetResultId(Tcl_Interp *interp, char *connid_c, PGresult *res){ Tcl_Channel conn_chan; Pg_ConnectionId *connid; int resid, i; char buf[32]; 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->res_last = 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 intgetresid(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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -