pgtclid.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 863 行 · 第 1/2 页

C
863
字号
/*------------------------------------------------------------------------- * * 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*) * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.43 2003/08/04 02:40:16 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres_fe.h"#include <errno.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;}#endif/* * The WatchProc and GetHandleProc are no-ops but must be present. */static voidPgWatchProc(ClientData instanceData, int mask){}static intPgGetHandleProc(ClientData instanceData, int direction,				ClientData *handlePtr){	return TCL_ERROR;}Tcl_ChannelType Pg_ConnType = {	"pgsql",					/* channel type */	NULL,						/* blockmodeproc */	PgDelConnectionId,			/* closeproc */	PgInputProc,				/* inputproc */	PgOutputProc,				/* outputproc */	NULL,						/* SeekProc, Not used */	NULL,						/* SetOptionProc, Not used */	NULL,						/* GetOptionProc, Not used */	PgWatchProc,				/* WatchProc, must be defined */	PgGetHandleProc,			/* GetHandleProc, must be defined */	NULL						/* Close2Proc, Not used */};/* * 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;	sprintf(connid->id, "pgsql%d", PQsocket(conn));#if TCL_MAJOR_VERSION >= 8	connid->notifier_channel = Tcl_MakeTcpClientChannel((ClientData) PQsocket(conn));	Tcl_RegisterChannel(NULL, connid->notifier_channel);#else	connid->notifier_socket = -1;#endif#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, CONST84 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);		if (connid_p)			*connid_p = NULL;		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(&notifies->notify_hash, &hsearch);			 entry != NULL;			 entry = Tcl_NextHashEntry(&hsearch))			ckfree((char *) Tcl_GetHashValue(entry));		Tcl_DeleteHashTable(&notifies->notify_hash);		if (notifies->conn_loss_cmd)			ckfree((void *) notifies->conn_loss_cmd);		if (notifies->interp)			Tcl_DontCallWhenDeleted(notifies->interp, PgNotifyInterpDelete,									(ClientData) notifies);		ckfree((void *) notifies);	}	/*	 * Turn off the Tcl event source for this connection, and delete any	 * pending notify and connection-loss events.	 */	PgStopNotifyEventSource(connid, true);	/* Close the libpq connection too */	PQfinish(connid->conn);	connid->conn = NULL;	/*	 * Kill the notifier channel, too.	We must not do this until after	 * we've closed the libpq connection, because Tcl will try to close	 * the socket itself!	 *	 * XXX Unfortunately, while this works fine if we are closing due to	 * explicit pg_disconnect, all Tcl versions through 8.4.1 dump core if	 * we try to do it during interpreter shutdown.  Not clear why. For	 * now, we kill the channel during pg_disconnect, but during interp	 * shutdown we just accept leakage of the (fairly small) amount of	 * memory taken for the channel state representation. (Note we are not	 * leaking a socket, since libpq closed that already.) We tell the	 * difference between pg_disconnect and interpreter shutdown by	 * testing for interp != NULL, which is an undocumented but apparently	 * safe way to tell.	 */#if TCL_MAJOR_VERSION >= 8	if (connid->notifier_channel != NULL && interp != NULL)		Tcl_UnregisterChannel(NULL, connid->notifier_channel);#endif	/*	 * 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, CONST84 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);	/* search, starting at slot after the last one used */	resid = connid->res_last;	for (;;)	{		/* advance, with wraparound */		if (++resid >= connid->res_max)			resid = 0;		/* this slot empty? */		if (!connid->results[resid])		{			connid->res_last = resid;			break;				/* success exit */		}		/* checked all slots? */		if (resid == connid->res_last)			break;				/* failure exit */	}	if (connid->results[resid])	{		/* no free slot found, so try to enlarge array */		if (connid->res_max >= connid->res_hardmax)		{			Tcl_SetResult(interp, "hard limit on result handles reached",						  TCL_STATIC);			return TCL_ERROR;		}		connid->res_last = 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, CONST84 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 */PGresult *

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?