agora inbox for [email protected]  
help / color / mirror / Atom feed
From: Andrew Chernow <[email protected]>
To: Patches (PostgreSQL) <[email protected]>
Subject: libpq events patch
Date: Wed, 03 Sep 2008 12:26:43 -0400
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>
References: <[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>

This is an updated version pf the libpqevents patch.  See

http://archives.postgresql.org/pgsql-hackers/2008-09/msg00153.php

for details.  The only change I didn't make yet is the event 'name'.  I 
have put it in and taken it out twice now, so a firm 'put it in there' 
would be appreciated.

Go here for libpqtypes using events.  pgfoundry is still using the older 
object hooks version.

http://libpqtypes.esilo.com/libpqtypes-events.tar.gz

-- 
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/

Index: src/interfaces/libpq/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/Makefile,v
retrieving revision 1.166
diff -C6 -r1.166 Makefile
*** src/interfaces/libpq/Makefile	16 Apr 2008 14:19:56 -0000	1.166
--- src/interfaces/libpq/Makefile	3 Sep 2008 16:06:49 -0000
***************
*** 29,41 ****
  # the object files from libpgport, this would not be true on all
  # platforms.
  LIBS := $(LIBS:-lpgport=)
  
  OBJS=	fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
  	fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
! 	md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o thread.o \
  	$(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
  
  ifeq ($(PORTNAME), cygwin)
  override shlib = cyg$(NAME)$(DLSUFFIX)
  endif
  
--- 29,41 ----
  # the object files from libpgport, this would not be true on all
  # platforms.
  LIBS := $(LIBS:-lpgport=)
  
  OBJS=	fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
  	fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
! 	md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o thread.o libpq-events.o \
  	$(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
  
  ifeq ($(PORTNAME), cygwin)
  override shlib = cyg$(NAME)$(DLSUFFIX)
  endif
  
***************
*** 103,114 ****
--- 103,115 ----
  
  $(top_builddir)/src/port/pg_config_paths.h:
  	$(MAKE) -C $(top_builddir)/src/port pg_config_paths.h
  
  install: all installdirs install-lib
  	$(INSTALL_DATA) $(srcdir)/libpq-fe.h '$(DESTDIR)$(includedir)'
+ 	$(INSTALL_DATA) $(srcdir)/libpq-events.h '$(DESTDIR)$(includedir)'
  	$(INSTALL_DATA) $(srcdir)/libpq-int.h '$(DESTDIR)$(includedir_internal)'
  	$(INSTALL_DATA) $(srcdir)/pqexpbuffer.h '$(DESTDIR)$(includedir_internal)'
  	$(INSTALL_DATA) $(srcdir)/pg_service.conf.sample '$(DESTDIR)$(datadir)/pg_service.conf.sample'
  
  installdirs: installdirs-lib
  	$(mkinstalldirs) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)'
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.19
diff -C6 -r1.19 exports.txt
*** src/interfaces/libpq/exports.txt	19 Mar 2008 00:39:33 -0000	1.19
--- src/interfaces/libpq/exports.txt	3 Sep 2008 16:06:49 -0000
***************
*** 138,143 ****
--- 138,154 ----
  PQsendDescribePortal      136
  lo_truncate               137
  PQconnectionUsedPassword  138
  pg_valid_server_encoding_id 139
  PQconnectionNeedsPassword 140
  lo_import_with_oid		  141
+ PQcopyResult              142
+ PQsetResultAttrs          143
+ PQsetvalue                144
+ PQresultAlloc             145
+ PQregisterEventProc       146
+ PQinstanceData            147
+ PQsetInstanceData         148
+ PQresultInstanceData      149
+ PQresultSetInstanceData   150
+ PQpassThroughData         151
+ PQresultPassThroughData   152
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.359
diff -C6 -r1.359 fe-connect.c
*** src/interfaces/libpq/fe-connect.c	29 May 2008 22:02:44 -0000	1.359
--- src/interfaces/libpq/fe-connect.c	3 Sep 2008 16:06:49 -0000
***************
*** 1971,1982 ****
--- 1971,2000 ----
   * release data that is to be held for the life of the PGconn structure.
   * If a value ought to be cleared/freed during PQreset(), do it there not here.
   */
  static void
  freePGconn(PGconn *conn)
  {
+ 	int i;
+ 	PGEventConnDestroy evt;
+ 
+ 	/* Let the event procs cleanup their state data */
+ 	for(i=0; i < conn->nEvents; i++)
+ 	{
+ 		evt.conn = conn;
+ 		(void)conn->events[i].proc(PGEVT_CONNDESTROY, &evt);
+ 	}
+ 
+ 	/* free the PGEvent array */
+ 	if(conn->events)
+ 	{
+ 		free(conn->events);
+ 		conn->events = NULL;
+ 		conn->nEvents = conn->eventArrSize = 0;
+ 	}
+ 
  	if (conn->pghost)
  		free(conn->pghost);
  	if (conn->pghostaddr)
  		free(conn->pghostaddr);
  	if (conn->pgport)
  		free(conn->pgport);
***************
*** 2152,2165 ****
  PQreset(PGconn *conn)
  {
  	if (conn)
  	{
  		closePGconn(conn);
  
! 		if (connectDBStart(conn))
! 			(void) connectDBComplete(conn);
  	}
  }
  
  
  /*
   * PQresetStart:
--- 2170,2200 ----
  PQreset(PGconn *conn)
  {
  	if (conn)
  	{
  		closePGconn(conn);
  
! 		if (connectDBStart(conn) && connectDBComplete(conn))
! 		{
! 			int i;
! 			PGEventConnReset evt;
! 
! 			for(i=0; i < conn->nEvents; i++)
! 			{
! 				evt.conn = conn;
! 
! 				if(!conn->events[i].proc(PGEVT_CONNRESET, &evt))
! 				{
! 					conn->status = CONNECTION_BAD;
! 					printfPQExpBuffer(&conn->errorMessage,
! 						libpq_gettext("PGEventProc %p failed during PGEVT_CONNRESET event\n"),
! 						conn->events[i].proc);
! 					break;
! 				}
! 			}
! 		}
  	}
  }
  
  
  /*
   * PQresetStart:
***************
*** 2187,2199 ****
   * closes the existing connection and makes a new one
   */
  PostgresPollingStatusType
  PQresetPoll(PGconn *conn)
  {
  	if (conn)
! 		return PQconnectPoll(conn);
  
  	return PGRES_POLLING_FAILED;
  }
  
  /*
   * PQcancelGet: get a PGcancel structure corresponding to a connection.
--- 2222,2258 ----
   * closes the existing connection and makes a new one
   */
  PostgresPollingStatusType
  PQresetPoll(PGconn *conn)
  {
  	if (conn)
! 	{
! 		PostgresPollingStatusType status = PQconnectPoll(conn);
! 
! 		if(status == PGRES_POLLING_OK)
! 		{
! 			int i;
! 			PGEventConnReset evt;
! 
! 			for(i=0; i < conn->nEvents; i++)
! 			{
! 				evt.conn = conn;
! 
! 				if(!conn->events[i].proc(PGEVT_CONNRESET, &evt))
! 				{
! 					conn->status = CONNECTION_BAD;
! 					printfPQExpBuffer(&conn->errorMessage,
! 						libpq_gettext("PGEventProc %p failed during PGEVT_CONNRESET event\n"),
! 						conn->events[i].proc);
! 					return PGRES_POLLING_FAILED;
! 				}
! 			}
! 		}
! 
! 		return status;
! 	}
  
  	return PGRES_POLLING_FAILED;
  }
  
  /*
   * PQcancelGet: get a PGcancel structure corresponding to a connection.
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.196
diff -C6 -r1.196 fe-exec.c
*** src/interfaces/libpq/fe-exec.c	23 Jun 2008 21:10:49 -0000	1.196
--- src/interfaces/libpq/fe-exec.c	3 Sep 2008 16:06:50 -0000
***************
*** 60,72 ****
  				int resultFormat);
  static void parseInput(PGconn *conn);
  static bool PQexecStart(PGconn *conn);
  static PGresult *PQexecFinish(PGconn *conn);
  static int PQsendDescribe(PGconn *conn, char desc_type,
  			   const char *desc_target);
! 
  
  /* ----------------
   * Space management for PGresult.
   *
   * Formerly, libpq did a separate malloc() for each field of each tuple
   * returned by a query.  This was remarkably expensive --- malloc/free
--- 60,72 ----
  				int resultFormat);
  static void parseInput(PGconn *conn);
  static bool PQexecStart(PGconn *conn);
  static PGresult *PQexecFinish(PGconn *conn);
  static int PQsendDescribe(PGconn *conn, char desc_type,
  			   const char *desc_target);
! static int check_field_number(const PGresult *res, int field_num);
  
  /* ----------------
   * Space management for PGresult.
   *
   * Formerly, libpq did a separate malloc() for each field of each tuple
   * returned by a query.  This was remarkably expensive --- malloc/free
***************
*** 121,141 ****
--- 121,167 ----
  #define PGRESULT_DATA_BLOCKSIZE		2048
  #define PGRESULT_ALIGN_BOUNDARY		MAXIMUM_ALIGNOF		/* from configure */
  #define PGRESULT_BLOCK_OVERHEAD		Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)
  #define PGRESULT_SEP_ALLOC_THRESHOLD	(PGRESULT_DATA_BLOCKSIZE / 2)
  
  
+ /* Does not duplicate the event instance data, sets this to NULL */
+ static PGEvent *
+ dupEvents(PGEvent *events, int count)
+ {
+ 	int i;
+ 	PGEvent *newEvents;
+ 
+ 	if(!events || count <= 0)
+ 		return NULL;
+ 
+ 	newEvents = (PGEvent *)malloc(count * sizeof(PGEvent));
+ 	if(!newEvents)
+ 		return NULL;
+ 
+ 	memcpy(newEvents, events, count * sizeof(PGEvent));
+ 
+ 	/* NULL out the data pointer */
+ 	for(i=0; i < count; i++)
+ 		newEvents[i].data = NULL;
+ 
+ 	return newEvents;
+ }
+ 
  /*
   * PQmakeEmptyPGresult
   *	 returns a newly allocated, initialized PGresult with given status.
   *	 If conn is not NULL and status indicates an error, the conn's
   *	 errorMessage is copied.
   *
   * Note this is exported --- you wouldn't think an application would need
   * to build its own PGresults, but this has proven useful in both libpgtcl
   * and the Perl5 interface, so maybe it's not so unreasonable.
+  *
+  * Updated April 2008 - If conn is not NULL, event states will be copied
+  * from the PGconn to the created PGresult.
   */
  
  PGresult *
  PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
  {
  	PGresult   *result;
***************
*** 157,175 ****
--- 183,216 ----
  	result->errMsg = NULL;
  	result->errFields = NULL;
  	result->null_field[0] = '\0';
  	result->curBlock = NULL;
  	result->curOffset = 0;
  	result->spaceLeft = 0;
+ 	result->nEvents = 0;
+ 	result->events = NULL;
  
  	if (conn)
  	{
  		/* copy connection data we might need for operations on PGresult */
  		result->noticeHooks = conn->noticeHooks;
  		result->client_encoding = conn->client_encoding;
  
+ 		/* copy events from connection */
+ 		if(conn->nEvents > 0)
+ 		{
+ 			result->events = dupEvents(conn->events, conn->nEvents);
+ 			if(!result->events)
+ 			{
+ 				PQclear(result);
+ 				return NULL;
+ 			}
+ 
+ 			result->nEvents = conn->nEvents;
+ 		}
+ 
  		/* consider copying conn's errorMessage */
  		switch (status)
  		{
  			case PGRES_EMPTY_QUERY:
  			case PGRES_COMMAND_OK:
  			case PGRES_TUPLES_OK:
***************
*** 192,203 ****
--- 233,487 ----
  		result->client_encoding = PG_SQL_ASCII;
  	}
  
  	return result;
  }
  
+ /* PQsetResultAttrs
+  * Set the attributes for a given result.  This function fails if there are
+  * already attributes contained in the provided result.  The call is
+  * ignored if numAttributes is is zero or attDescs is NULL.  If the
+  * function fails, it returns zero.  If the function succeeds, it
+  * returns a non-zero value.
+  */
+ int
+ PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
+ {
+ 	int i;
+ 
+ 	/* If attrs already exist, they cannot be overwritten. */
+ 	if(!res || res->numAttributes > 0)
+ 		return FALSE;
+ 
+ 	/* ignore request */
+ 	if(numAttributes <= 0 || !attDescs)
+ 		return TRUE;
+ 
+ 	res->attDescs = (PGresAttDesc *)PQresultAlloc(res,
+ 		numAttributes * sizeof(PGresAttDesc));
+ 
+ 	if(!res->attDescs)
+ 		return FALSE;
+ 
+ 	res->numAttributes = numAttributes;
+ 	memcpy(res->attDescs, attDescs,
+ 		numAttributes * sizeof(PGresAttDesc));
+ 
+ 	/* resultalloc the attribute names.  The above memcpy has the attr
+ 	 * names pointing at the source result's private memory (or at the
+ 	 * callers provided attDescs memory).
+ 	 */
+ 	res->binary = 1;
+ 	for(i=0; i < res->numAttributes; i++)
+ 	{
+ 		if(res->attDescs[i].name)
+ 			res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name);
+ 		else
+ 			res->attDescs[i].name = res->null_field;
+ 
+ 		if(!res->attDescs[i].name)
+ 			return FALSE;
+ 
+ 		/* Although deprecated, because results can have text+binary columns,
+ 		 * its easy enough to deduce so set it for completeness.
+ 		 */
+ 		if(res->attDescs[i].format == 0)
+ 			res->binary = 0;
+ 	}
+ 
+ 	return TRUE;
+ }
+ 
+ /*
+  * PQcopyResult
+  * Returns a deep copy of the provided 'src' PGresult, which cannot be NULL.
+  * The 'options' argument controls which portions of the result will or will
+  * NOT be copied.  The created result is always put into the
+  * PGRES_TUPLES_OK status.  The source result error message is not copied,
+  * although cmdStatus is.
+  *
+  * To set custom attributes, see PQsetResultAttrs.  That function requires
+  * that there are no attrs contained in the result, so to use that
+  * function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES
+  * options with this function.
+  *
+  * Options:
+  *   PG_COPYRES_ATTRS - Copy the source result's attributes
+  *
+  *   PG_COPYRES_TUPLES - Copy the source result's tuples.  This implies
+  *   copying the attrs, being how the attrs are needed by the tuples.
+  *
+  *   PG_COPYRES_EVENTS - Copy the source result's events.
+  *
+  *   PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks.
+  */
+ 
+ PGresult *
+ PQcopyResult(const PGresult *src, int options)
+ {
+ 	int i;
+ 	PGresult *dest;
+ 	PGEventResultCopy evt;
+ 
+ 	if(!src)
+ 		return NULL;
+ 
+ 	/* Automatically turn on attrs options because you can't copy tuples
+ 	 * without copying the attrs.  _TUPLES implies _ATTRS.
+ 	 */
+ 	if(options & PG_COPYRES_TUPLES)
+ 		options |= PG_COPYRES_ATTRS;
+ 
+ 	dest = PQmakeEmptyPGresult((PGconn *)NULL, PGRES_TUPLES_OK);
+ 	if(!dest)
+ 		return NULL;
+ 
+ 	/* always copy these over.  Is cmdStatus useful here? */
+ 	dest->client_encoding = src->client_encoding;
+ 	strcpy(dest->cmdStatus, src->cmdStatus);
+ 
+ 	/* Wants to copy notice hooks */
+ 	if(options & PG_COPYRES_NOTICEHOOKS)
+ 		dest->noticeHooks = src->noticeHooks;
+ 
+ 	/* Wants attrs */
+ 	if((options & PG_COPYRES_ATTRS) &&
+ 		 !PQsetResultAttrs(dest, src->numAttributes, src->attDescs))
+ 	{
+ 		PQclear(dest);
+ 		return NULL;
+ 	}
+ 
+ 	/* Wants to copy result tuples: use PQsetvalue(). */
+ 	if((options & PG_COPYRES_TUPLES) && src->ntups > 0)
+ 	{
+ 		int tup, field;
+ 		for(tup=0; tup < src->ntups; tup++)
+ 			for(field=0; field < src->numAttributes; field++)
+ 				PQsetvalue(dest, tup, field, src->tuples[tup][field].value,
+ 					src->tuples[tup][field].len);
+ 	}
+ 
+ 	/* Wants to copy PGEvents. */
+ 	if((options & PG_COPYRES_EVENTS) && src->nEvents > 0)
+ 	{
+ 		dest->events = dupEvents(src->events, src->nEvents);
+ 		if(!dest->events)
+ 		{
+ 			PQclear(dest);
+ 			return NULL;
+ 		}
+ 
+ 		dest->nEvents = src->nEvents;
+ 	}
+ 
+ 	/* Trigger PGEVT_RESULTCOPY event */
+ 	for(i=0; i < dest->nEvents; i++)
+ 	{
+ 		evt.src = src;
+ 		evt.dest = dest;
+ 		if(!dest->events[i].proc(PGEVT_RESULTCOPY, &evt))
+ 		{
+ 			PQclear(dest);
+ 			return NULL;
+ 		}
+ 	}
+ 
+ 	return dest;
+ }
+ 
+ int
+ PQsetvalue(PGresult *res, int tup_num, int field_num,
+ 	char *value, int len)
+ {
+ 	PGresAttValue *attval;
+ 
+ 	if(!check_field_number(res, field_num))
+ 		return FALSE;
+ 
+ 	/* Invalid tup_num, must be <= ntups */
+ 	if(tup_num > res->ntups)
+ 		return FALSE;
+ 
+ 	/* need to grow the tuple table */
+ 	if(res->ntups >= res->tupArrSize)
+ 	{
+ 		int n = res->tupArrSize ? (res->tupArrSize*3)/2 : 64;
+ 		PGresAttValue **tups = (PGresAttValue **)
+ 			(res->tuples ? realloc(res->tuples, n*sizeof(PGresAttValue *)) :
+ 			 malloc(n*sizeof(PGresAttValue *)));
+ 
+ 		if(!tups)
+ 			return FALSE;
+ 
+ 		memset(tups + res->tupArrSize, 0,
+ 			(n - res->tupArrSize) * sizeof(PGresAttValue *));
+ 		res->tuples = tups;
+ 		res->tupArrSize = n;
+ 	}
+ 
+ 	/* need to allocate a new tuple */
+ 	if(tup_num == res->ntups && !res->tuples[tup_num])
+ 	{
+ 		int i;
+ 		PGresAttValue *tup = (PGresAttValue *)PQresultAlloc(
+ 			res, res->numAttributes * sizeof(PGresAttValue));
+ 
+ 		if(!tup)
+ 			return FALSE;
+ 
+ 		/* initialize each column to NULL */
+ 		for(i=0; i < res->numAttributes; i++)
+ 		{
+ 			tup[i].len = NULL_LEN;
+ 			tup[i].value = res->null_field;
+ 		}
+ 
+ 		res->tuples[tup_num] = tup;
+ 		res->ntups++;
+ 	}
+ 
+ 	attval = &res->tuples[tup_num][field_num];
+ 
+ 	/* On top of NULL_LEN, treat a NULL value as a NULL field */
+ 	if(len == NULL_LEN || value == NULL)
+ 	{
+ 		attval->len = NULL_LEN;
+ 		attval->value = res->null_field;
+ 	}
+ 	else
+ 	{
+ 		if(len < 0)
+ 			len = 0;
+ 
+ 		if(len == 0)
+ 		{
+ 			attval->len = 0;
+ 			attval->value = res->null_field;
+ 		}
+ 		else
+ 		{
+ 			attval->value = (char *)PQresultAlloc(res, len + 1);
+ 			if(!attval->value)
+ 				return FALSE;
+ 
+ 			attval->len = len;
+ 			memcpy(attval->value, value, len);
+ 			attval->value[len] = '\0';
+ 		}
+ 	}
+ 
+ 	return TRUE;
+ }
+ 
+ void *
+ PQresultAlloc(PGresult *res, size_t nBytes)
+ {
+ 	return pqResultAlloc(res, nBytes, TRUE);
+ }
+ 
  /*
   * pqResultAlloc -
   *		Allocate subsidiary storage for a PGresult.
   *
   * nBytes is the amount of space needed for the object.
   * If isBinary is true, we assume that we need to align the object on
***************
*** 349,365 ****
--- 633,664 ----
   * PQclear -
   *	  free's the memory associated with a PGresult
   */
  void
  PQclear(PGresult *res)
  {
+ 	int i;
  	PGresult_data *block;
+ 	PGEventResultDestroy evt;
  
  	if (!res)
  		return;
  
+ 	for(i=0; i < res->nEvents; i++)
+ 	{
+ 		evt.result = res;
+ 		(void)res->events[i].proc(PGEVT_RESULTDESTROY, &evt);
+ 	}
+ 
+ 	if(res->events)
+ 	{
+ 		free(res->events);
+ 		res->events = NULL;
+ 		res->nEvents = 0;
+ 	}
+ 
  	/* Free all the subsidiary blocks */
  	while ((block = res->curBlock) != NULL)
  	{
  		res->curBlock = block->next;
  		free(block);
  	}
***************
*** 1192,1204 ****
   *	  memory).
   */
  
  PGresult *
  PQgetResult(PGconn *conn)
  {
! 	PGresult   *res;
  
  	if (!conn)
  		return NULL;
  
  	/* Parse any available data, if our state permits. */
  	parseInput(conn);
--- 1491,1503 ----
   *	  memory).
   */
  
  PGresult *
  PQgetResult(PGconn *conn)
  {
! 	PGresult   *res=NULL;
  
  	if (!conn)
  		return NULL;
  
  	/* Parse any available data, if our state permits. */
  	parseInput(conn);
***************
*** 1267,1278 ****
--- 1566,1602 ----
  							  libpq_gettext("unexpected asyncStatus: %d\n"),
  							  (int) conn->asyncStatus);
  			res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
  			break;
  	}
  
+ 	if(res && (res->resultStatus == PGRES_COMMAND_OK ||
+ 		 res->resultStatus == PGRES_TUPLES_OK ||
+ 		 res->resultStatus == PGRES_EMPTY_QUERY))
+ 	{
+ 		int i;
+ 		PGEventResultCreate evt;
+ 
+ 		for(i=0; i < res->nEvents; i++)
+ 		{
+ 			evt.conn = conn;
+ 			evt.result = res;
+ 
+ 			if(!res->events[i].proc(PGEVT_RESULTCREATE, &evt))
+ 			{
+ 				char msg[256];
+ 				sprintf(msg,
+ 					"PGEventProc %p failed during PGEVT_RESULTCREATE event",
+ 					res->events[i].proc);
+ 				pqSetResultError(res, msg);
+ 				res->resultStatus = PGRES_FATAL_ERROR;
+ 				break;
+ 			}
+ 		}
+ 	}
+ 
  	return res;
  }
  
  
  /*
   * PQexec
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.142
diff -C6 -r1.142 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h	19 Mar 2008 00:39:33 -0000	1.142
--- src/interfaces/libpq/libpq-fe.h	3 Sep 2008 16:06:50 -0000
***************
*** 25,36 ****
--- 25,45 ----
  /*
   * postgres_ext.h defines the backend's externally visible types,
   * such as Oid.
   */
  #include "postgres_ext.h"
  
+ /* -----------------------
+  * Options for PQcopyResult
+  */
+ 
+ #define PG_COPYRES_ATTRS          0x01
+ #define PG_COPYRES_TUPLES         0x02 /* Implies PG_COPYRES_ATTRS */
+ #define PG_COPYRES_EVENTS         0x04
+ #define PG_COPYRES_NOTICEHOOKS    0x08
+ 
  /* Application-visible enum types */
  
  typedef enum
  {
  	/*
  	 * Although it is okay to add to this list, values which become unused
***************
*** 190,201 ****
--- 199,225 ----
  		int		   *ptr;		/* can't use void (dec compiler barfs)	 */
  		int			integer;
  	}			u;
  } PQArgBlock;
  
  /* ----------------
+  * PGresAttDesc -- Data about a single attribute (column) of a query result
+  * ----------------
+  */
+ typedef struct pgresAttDesc
+ {
+ 	char	   *name;			/* column name */
+ 	Oid			tableid;		/* source table, if known */
+ 	int			columnid;		/* source column, if known */
+ 	int			format;			/* format code for value (text/binary) */
+ 	Oid			typid;			/* type id */
+ 	int			typlen;			/* type size */
+ 	int			atttypmod;  /* type-specific modifier info */
+ } PGresAttDesc;
+ 
+ /* ----------------
   * Exported functions of libpq
   * ----------------
   */
  
  /* ===	in fe-connect.c === */
  
***************
*** 434,445 ****
--- 458,490 ----
   * Make an empty PGresult with given status (some apps find this
   * useful). If conn is not NULL and status indicates an error, the
   * conn's errorMessage is copied.
   */
  extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
  
+ extern PGresult *
+ PQcopyResult(const PGresult *src, int options);
+ 
+ extern int
+ PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);
+ 
+ extern void *
+ PQresultAlloc(PGresult *res, size_t nBytes);
+ 
+ /*
+  * Sets the value for a tuple field.  The tup_num must be less than or
+  * equal to PQntuples(res).  This function will generate tuples as needed.
+  * A new tuple is generated when tup_num equals PQntuples(res) and there
+  * are no fields defined for that tuple.
+  *
+  * Returns a non-zero value for success and zero for failure.
+  */
+ extern int
+ PQsetvalue(PGresult *res, int tup_num, int field_num,
+ 	char *value, int len);
+ 
  
  /* Quoting strings before inclusion in queries. */
  extern size_t PQescapeStringConn(PGconn *conn,
  				   char *to, const char *from, size_t length,
  				   int *error);
  extern unsigned char *PQescapeByteaConn(PGconn *conn,
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.131
diff -C6 -r1.131 libpq-int.h
*** src/interfaces/libpq/libpq-int.h	29 May 2008 22:02:44 -0000	1.131
--- src/interfaces/libpq/libpq-int.h	3 Sep 2008 16:06:50 -0000
***************
*** 19,30 ****
--- 19,31 ----
  
  #ifndef LIBPQ_INT_H
  #define LIBPQ_INT_H
  
  /* We assume libpq-fe.h has already been included. */
  #include "postgres_fe.h"
+ #include "libpq-events.h"
  
  #include <time.h>
  #include <sys/types.h>
  #ifndef WIN32
  #include <sys/time.h>
  #endif
***************
*** 97,121 ****
  union pgresult_data
  {
  	PGresult_data *next;		/* link to next block, or NULL */
  	char		space[1];		/* dummy for accessing block as bytes */
  };
  
- /* Data about a single attribute (column) of a query result */
- 
- typedef struct pgresAttDesc
- {
- 	char	   *name;			/* column name */
- 	Oid			tableid;		/* source table, if known */
- 	int			columnid;		/* source column, if known */
- 	int			format;			/* format code for value (text/binary) */
- 	Oid			typid;			/* type id */
- 	int			typlen;			/* type size */
- 	int			atttypmod;		/* type-specific modifier info */
- } PGresAttDesc;
- 
  /* Data about a single parameter of a prepared statement */
  typedef struct pgresParamDesc
  {
  	Oid			typid;			/* type id */
  } PGresParamDesc;
  
--- 98,109 ----
***************
*** 159,170 ****
--- 147,167 ----
  	PQnoticeReceiver noticeRec; /* notice message receiver */
  	void	   *noticeRecArg;
  	PQnoticeProcessor noticeProc;		/* notice message processor */
  	void	   *noticeProcArg;
  } PGNoticeHooks;
  
+ typedef struct
+ {
+ 	/* pointer supplied by user */
+ 	void *passThrough;
+ 	/* state (instance) data, optionally generated by event proc */
+ 	void *data;
+ 	PGEventProc proc;
+ } PGEvent;
+ 
  struct pg_result
  {
  	int			ntups;
  	int			numAttributes;
  	PGresAttDesc *attDescs;
  	PGresAttValue **tuples;		/* each PGresTuple is an array of
***************
*** 181,192 ****
--- 178,193 ----
  	 * These fields are copied from the originating PGconn, so that operations
  	 * on the PGresult don't have to reference the PGconn.
  	 */
  	PGNoticeHooks noticeHooks;
  	int			client_encoding;	/* encoding id */
  
+ 	/* registered events, copied from conn */
+ 	int nEvents;
+ 	PGEvent *events;
+ 
  	/*
  	 * Error information (all NULL if not an error result).  errMsg is the
  	 * "overall" error message returned by PQresultErrorMessage.  If we have
  	 * per-field info then it is stored in a linked list.
  	 */
  	char	   *errMsg;			/* error message, or NULL if no error */
***************
*** 300,311 ****
--- 301,317 ----
  	/* Optional file to write trace info to */
  	FILE	   *Pfdebug;
  
  	/* Callback procedures for notice message processing */
  	PGNoticeHooks noticeHooks;
  
+ 	/* registered events via PQregisterEventProc */
+ 	int nEvents;
+ 	int eventArrSize;
+ 	PGEvent *events;
+ 
  	/* Status indicators */
  	ConnStatusType status;
  	PGAsyncStatusType asyncStatus;
  	PGTransactionStatusType xactStatus; /* never changes to ACTIVE */
  	PGQueryClass queryclass;
  	char	   *last_query;		/* last SQL command, or NULL if unknown */
Index: src/interfaces/libpq/pthread-win32.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/pthread-win32.c,v
retrieving revision 1.17
diff -C6 -r1.17 pthread-win32.c
*** src/interfaces/libpq/pthread-win32.c	21 May 2008 14:20:48 -0000	1.17
--- src/interfaces/libpq/pthread-win32.c	3 Sep 2008 16:06:50 -0000
***************
*** 2,14 ****
  *
  * pthread-win32.c
  *	 partial pthread implementation for win32
  *
  * Copyright (c) 2004-2008, PostgreSQL Global Development Group
  * IDENTIFICATION
! *	$PostgreSQL: pgsql/src/interfaces/libpq/pthread-win32.c,v 1.17 2008/05/21 14:20:48 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
  
  #include "postgres_fe.h"
  
--- 2,14 ----
  *
  * pthread-win32.c
  *	 partial pthread implementation for win32
  *
  * Copyright (c) 2004-2008, PostgreSQL Global Development Group
  * IDENTIFICATION
! *	$PostgreSQL: pgsql/src/interfaces/libpq/pthread-win32.c,v 1.16 2008/05/16 18:30:53 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
  
  #include "postgres_fe.h"
  


Attachments:

  [text/plain] libpq-events.patch (25.4K, ../[email protected]/2-libpq-events.patch)
  download | inline diff:
Index: src/interfaces/libpq/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/Makefile,v
retrieving revision 1.166
diff -C6 -r1.166 Makefile
*** src/interfaces/libpq/Makefile	16 Apr 2008 14:19:56 -0000	1.166
--- src/interfaces/libpq/Makefile	3 Sep 2008 16:06:49 -0000
***************
*** 29,41 ****
  # the object files from libpgport, this would not be true on all
  # platforms.
  LIBS := $(LIBS:-lpgport=)
  
  OBJS=	fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
  	fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
! 	md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o thread.o \
  	$(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
  
  ifeq ($(PORTNAME), cygwin)
  override shlib = cyg$(NAME)$(DLSUFFIX)
  endif
  
--- 29,41 ----
  # the object files from libpgport, this would not be true on all
  # platforms.
  LIBS := $(LIBS:-lpgport=)
  
  OBJS=	fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
  	fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
! 	md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o thread.o libpq-events.o \
  	$(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
  
  ifeq ($(PORTNAME), cygwin)
  override shlib = cyg$(NAME)$(DLSUFFIX)
  endif
  
***************
*** 103,114 ****
--- 103,115 ----
  
  $(top_builddir)/src/port/pg_config_paths.h:
  	$(MAKE) -C $(top_builddir)/src/port pg_config_paths.h
  
  install: all installdirs install-lib
  	$(INSTALL_DATA) $(srcdir)/libpq-fe.h '$(DESTDIR)$(includedir)'
+ 	$(INSTALL_DATA) $(srcdir)/libpq-events.h '$(DESTDIR)$(includedir)'
  	$(INSTALL_DATA) $(srcdir)/libpq-int.h '$(DESTDIR)$(includedir_internal)'
  	$(INSTALL_DATA) $(srcdir)/pqexpbuffer.h '$(DESTDIR)$(includedir_internal)'
  	$(INSTALL_DATA) $(srcdir)/pg_service.conf.sample '$(DESTDIR)$(datadir)/pg_service.conf.sample'
  
  installdirs: installdirs-lib
  	$(mkinstalldirs) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)'
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.19
diff -C6 -r1.19 exports.txt
*** src/interfaces/libpq/exports.txt	19 Mar 2008 00:39:33 -0000	1.19
--- src/interfaces/libpq/exports.txt	3 Sep 2008 16:06:49 -0000
***************
*** 138,143 ****
--- 138,154 ----
  PQsendDescribePortal      136
  lo_truncate               137
  PQconnectionUsedPassword  138
  pg_valid_server_encoding_id 139
  PQconnectionNeedsPassword 140
  lo_import_with_oid		  141
+ PQcopyResult              142
+ PQsetResultAttrs          143
+ PQsetvalue                144
+ PQresultAlloc             145
+ PQregisterEventProc       146
+ PQinstanceData            147
+ PQsetInstanceData         148
+ PQresultInstanceData      149
+ PQresultSetInstanceData   150
+ PQpassThroughData         151
+ PQresultPassThroughData   152
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.359
diff -C6 -r1.359 fe-connect.c
*** src/interfaces/libpq/fe-connect.c	29 May 2008 22:02:44 -0000	1.359
--- src/interfaces/libpq/fe-connect.c	3 Sep 2008 16:06:49 -0000
***************
*** 1971,1982 ****
--- 1971,2000 ----
   * release data that is to be held for the life of the PGconn structure.
   * If a value ought to be cleared/freed during PQreset(), do it there not here.
   */
  static void
  freePGconn(PGconn *conn)
  {
+ 	int i;
+ 	PGEventConnDestroy evt;
+ 
+ 	/* Let the event procs cleanup their state data */
+ 	for(i=0; i < conn->nEvents; i++)
+ 	{
+ 		evt.conn = conn;
+ 		(void)conn->events[i].proc(PGEVT_CONNDESTROY, &evt);
+ 	}
+ 
+ 	/* free the PGEvent array */
+ 	if(conn->events)
+ 	{
+ 		free(conn->events);
+ 		conn->events = NULL;
+ 		conn->nEvents = conn->eventArrSize = 0;
+ 	}
+ 
  	if (conn->pghost)
  		free(conn->pghost);
  	if (conn->pghostaddr)
  		free(conn->pghostaddr);
  	if (conn->pgport)
  		free(conn->pgport);
***************
*** 2152,2165 ****
  PQreset(PGconn *conn)
  {
  	if (conn)
  	{
  		closePGconn(conn);
  
! 		if (connectDBStart(conn))
! 			(void) connectDBComplete(conn);
  	}
  }
  
  
  /*
   * PQresetStart:
--- 2170,2200 ----
  PQreset(PGconn *conn)
  {
  	if (conn)
  	{
  		closePGconn(conn);
  
! 		if (connectDBStart(conn) && connectDBComplete(conn))
! 		{
! 			int i;
! 			PGEventConnReset evt;
! 
! 			for(i=0; i < conn->nEvents; i++)
! 			{
! 				evt.conn = conn;
! 
! 				if(!conn->events[i].proc(PGEVT_CONNRESET, &evt))
! 				{
! 					conn->status = CONNECTION_BAD;
! 					printfPQExpBuffer(&conn->errorMessage,
! 						libpq_gettext("PGEventProc %p failed during PGEVT_CONNRESET event\n"),
! 						conn->events[i].proc);
! 					break;
! 				}
! 			}
! 		}
  	}
  }
  
  
  /*
   * PQresetStart:
***************
*** 2187,2199 ****
   * closes the existing connection and makes a new one
   */
  PostgresPollingStatusType
  PQresetPoll(PGconn *conn)
  {
  	if (conn)
! 		return PQconnectPoll(conn);
  
  	return PGRES_POLLING_FAILED;
  }
  
  /*
   * PQcancelGet: get a PGcancel structure corresponding to a connection.
--- 2222,2258 ----
   * closes the existing connection and makes a new one
   */
  PostgresPollingStatusType
  PQresetPoll(PGconn *conn)
  {
  	if (conn)
! 	{
! 		PostgresPollingStatusType status = PQconnectPoll(conn);
! 
! 		if(status == PGRES_POLLING_OK)
! 		{
! 			int i;
! 			PGEventConnReset evt;
! 
! 			for(i=0; i < conn->nEvents; i++)
! 			{
! 				evt.conn = conn;
! 
! 				if(!conn->events[i].proc(PGEVT_CONNRESET, &evt))
! 				{
! 					conn->status = CONNECTION_BAD;
! 					printfPQExpBuffer(&conn->errorMessage,
! 						libpq_gettext("PGEventProc %p failed during PGEVT_CONNRESET event\n"),
! 						conn->events[i].proc);
! 					return PGRES_POLLING_FAILED;
! 				}
! 			}
! 		}
! 
! 		return status;
! 	}
  
  	return PGRES_POLLING_FAILED;
  }
  
  /*
   * PQcancelGet: get a PGcancel structure corresponding to a connection.
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.196
diff -C6 -r1.196 fe-exec.c
*** src/interfaces/libpq/fe-exec.c	23 Jun 2008 21:10:49 -0000	1.196
--- src/interfaces/libpq/fe-exec.c	3 Sep 2008 16:06:50 -0000
***************
*** 60,72 ****
  				int resultFormat);
  static void parseInput(PGconn *conn);
  static bool PQexecStart(PGconn *conn);
  static PGresult *PQexecFinish(PGconn *conn);
  static int PQsendDescribe(PGconn *conn, char desc_type,
  			   const char *desc_target);
! 
  
  /* ----------------
   * Space management for PGresult.
   *
   * Formerly, libpq did a separate malloc() for each field of each tuple
   * returned by a query.  This was remarkably expensive --- malloc/free
--- 60,72 ----
  				int resultFormat);
  static void parseInput(PGconn *conn);
  static bool PQexecStart(PGconn *conn);
  static PGresult *PQexecFinish(PGconn *conn);
  static int PQsendDescribe(PGconn *conn, char desc_type,
  			   const char *desc_target);
! static int check_field_number(const PGresult *res, int field_num);
  
  /* ----------------
   * Space management for PGresult.
   *
   * Formerly, libpq did a separate malloc() for each field of each tuple
   * returned by a query.  This was remarkably expensive --- malloc/free
***************
*** 121,141 ****
--- 121,167 ----
  #define PGRESULT_DATA_BLOCKSIZE		2048
  #define PGRESULT_ALIGN_BOUNDARY		MAXIMUM_ALIGNOF		/* from configure */
  #define PGRESULT_BLOCK_OVERHEAD		Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)
  #define PGRESULT_SEP_ALLOC_THRESHOLD	(PGRESULT_DATA_BLOCKSIZE / 2)
  
  
+ /* Does not duplicate the event instance data, sets this to NULL */
+ static PGEvent *
+ dupEvents(PGEvent *events, int count)
+ {
+ 	int i;
+ 	PGEvent *newEvents;
+ 
+ 	if(!events || count <= 0)
+ 		return NULL;
+ 
+ 	newEvents = (PGEvent *)malloc(count * sizeof(PGEvent));
+ 	if(!newEvents)
+ 		return NULL;
+ 
+ 	memcpy(newEvents, events, count * sizeof(PGEvent));
+ 
+ 	/* NULL out the data pointer */
+ 	for(i=0; i < count; i++)
+ 		newEvents[i].data = NULL;
+ 
+ 	return newEvents;
+ }
+ 
  /*
   * PQmakeEmptyPGresult
   *	 returns a newly allocated, initialized PGresult with given status.
   *	 If conn is not NULL and status indicates an error, the conn's
   *	 errorMessage is copied.
   *
   * Note this is exported --- you wouldn't think an application would need
   * to build its own PGresults, but this has proven useful in both libpgtcl
   * and the Perl5 interface, so maybe it's not so unreasonable.
+  *
+  * Updated April 2008 - If conn is not NULL, event states will be copied
+  * from the PGconn to the created PGresult.
   */
  
  PGresult *
  PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
  {
  	PGresult   *result;
***************
*** 157,175 ****
--- 183,216 ----
  	result->errMsg = NULL;
  	result->errFields = NULL;
  	result->null_field[0] = '\0';
  	result->curBlock = NULL;
  	result->curOffset = 0;
  	result->spaceLeft = 0;
+ 	result->nEvents = 0;
+ 	result->events = NULL;
  
  	if (conn)
  	{
  		/* copy connection data we might need for operations on PGresult */
  		result->noticeHooks = conn->noticeHooks;
  		result->client_encoding = conn->client_encoding;
  
+ 		/* copy events from connection */
+ 		if(conn->nEvents > 0)
+ 		{
+ 			result->events = dupEvents(conn->events, conn->nEvents);
+ 			if(!result->events)
+ 			{
+ 				PQclear(result);
+ 				return NULL;
+ 			}
+ 
+ 			result->nEvents = conn->nEvents;
+ 		}
+ 
  		/* consider copying conn's errorMessage */
  		switch (status)
  		{
  			case PGRES_EMPTY_QUERY:
  			case PGRES_COMMAND_OK:
  			case PGRES_TUPLES_OK:
***************
*** 192,203 ****
--- 233,487 ----
  		result->client_encoding = PG_SQL_ASCII;
  	}
  
  	return result;
  }
  
+ /* PQsetResultAttrs
+  * Set the attributes for a given result.  This function fails if there are
+  * already attributes contained in the provided result.  The call is
+  * ignored if numAttributes is is zero or attDescs is NULL.  If the
+  * function fails, it returns zero.  If the function succeeds, it
+  * returns a non-zero value.
+  */
+ int
+ PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
+ {
+ 	int i;
+ 
+ 	/* If attrs already exist, they cannot be overwritten. */
+ 	if(!res || res->numAttributes > 0)
+ 		return FALSE;
+ 
+ 	/* ignore request */
+ 	if(numAttributes <= 0 || !attDescs)
+ 		return TRUE;
+ 
+ 	res->attDescs = (PGresAttDesc *)PQresultAlloc(res,
+ 		numAttributes * sizeof(PGresAttDesc));
+ 
+ 	if(!res->attDescs)
+ 		return FALSE;
+ 
+ 	res->numAttributes = numAttributes;
+ 	memcpy(res->attDescs, attDescs,
+ 		numAttributes * sizeof(PGresAttDesc));
+ 
+ 	/* resultalloc the attribute names.  The above memcpy has the attr
+ 	 * names pointing at the source result's private memory (or at the
+ 	 * callers provided attDescs memory).
+ 	 */
+ 	res->binary = 1;
+ 	for(i=0; i < res->numAttributes; i++)
+ 	{
+ 		if(res->attDescs[i].name)
+ 			res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name);
+ 		else
+ 			res->attDescs[i].name = res->null_field;
+ 
+ 		if(!res->attDescs[i].name)
+ 			return FALSE;
+ 
+ 		/* Although deprecated, because results can have text+binary columns,
+ 		 * its easy enough to deduce so set it for completeness.
+ 		 */
+ 		if(res->attDescs[i].format == 0)
+ 			res->binary = 0;
+ 	}
+ 
+ 	return TRUE;
+ }
+ 
+ /*
+  * PQcopyResult
+  * Returns a deep copy of the provided 'src' PGresult, which cannot be NULL.
+  * The 'options' argument controls which portions of the result will or will
+  * NOT be copied.  The created result is always put into the
+  * PGRES_TUPLES_OK status.  The source result error message is not copied,
+  * although cmdStatus is.
+  *
+  * To set custom attributes, see PQsetResultAttrs.  That function requires
+  * that there are no attrs contained in the result, so to use that
+  * function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES
+  * options with this function.
+  *
+  * Options:
+  *   PG_COPYRES_ATTRS - Copy the source result's attributes
+  *
+  *   PG_COPYRES_TUPLES - Copy the source result's tuples.  This implies
+  *   copying the attrs, being how the attrs are needed by the tuples.
+  *
+  *   PG_COPYRES_EVENTS - Copy the source result's events.
+  *
+  *   PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks.
+  */
+ 
+ PGresult *
+ PQcopyResult(const PGresult *src, int options)
+ {
+ 	int i;
+ 	PGresult *dest;
+ 	PGEventResultCopy evt;
+ 
+ 	if(!src)
+ 		return NULL;
+ 
+ 	/* Automatically turn on attrs options because you can't copy tuples
+ 	 * without copying the attrs.  _TUPLES implies _ATTRS.
+ 	 */
+ 	if(options & PG_COPYRES_TUPLES)
+ 		options |= PG_COPYRES_ATTRS;
+ 
+ 	dest = PQmakeEmptyPGresult((PGconn *)NULL, PGRES_TUPLES_OK);
+ 	if(!dest)
+ 		return NULL;
+ 
+ 	/* always copy these over.  Is cmdStatus useful here? */
+ 	dest->client_encoding = src->client_encoding;
+ 	strcpy(dest->cmdStatus, src->cmdStatus);
+ 
+ 	/* Wants to copy notice hooks */
+ 	if(options & PG_COPYRES_NOTICEHOOKS)
+ 		dest->noticeHooks = src->noticeHooks;
+ 
+ 	/* Wants attrs */
+ 	if((options & PG_COPYRES_ATTRS) &&
+ 		 !PQsetResultAttrs(dest, src->numAttributes, src->attDescs))
+ 	{
+ 		PQclear(dest);
+ 		return NULL;
+ 	}
+ 
+ 	/* Wants to copy result tuples: use PQsetvalue(). */
+ 	if((options & PG_COPYRES_TUPLES) && src->ntups > 0)
+ 	{
+ 		int tup, field;
+ 		for(tup=0; tup < src->ntups; tup++)
+ 			for(field=0; field < src->numAttributes; field++)
+ 				PQsetvalue(dest, tup, field, src->tuples[tup][field].value,
+ 					src->tuples[tup][field].len);
+ 	}
+ 
+ 	/* Wants to copy PGEvents. */
+ 	if((options & PG_COPYRES_EVENTS) && src->nEvents > 0)
+ 	{
+ 		dest->events = dupEvents(src->events, src->nEvents);
+ 		if(!dest->events)
+ 		{
+ 			PQclear(dest);
+ 			return NULL;
+ 		}
+ 
+ 		dest->nEvents = src->nEvents;
+ 	}
+ 
+ 	/* Trigger PGEVT_RESULTCOPY event */
+ 	for(i=0; i < dest->nEvents; i++)
+ 	{
+ 		evt.src = src;
+ 		evt.dest = dest;
+ 		if(!dest->events[i].proc(PGEVT_RESULTCOPY, &evt))
+ 		{
+ 			PQclear(dest);
+ 			return NULL;
+ 		}
+ 	}
+ 
+ 	return dest;
+ }
+ 
+ int
+ PQsetvalue(PGresult *res, int tup_num, int field_num,
+ 	char *value, int len)
+ {
+ 	PGresAttValue *attval;
+ 
+ 	if(!check_field_number(res, field_num))
+ 		return FALSE;
+ 
+ 	/* Invalid tup_num, must be <= ntups */
+ 	if(tup_num > res->ntups)
+ 		return FALSE;
+ 
+ 	/* need to grow the tuple table */
+ 	if(res->ntups >= res->tupArrSize)
+ 	{
+ 		int n = res->tupArrSize ? (res->tupArrSize*3)/2 : 64;
+ 		PGresAttValue **tups = (PGresAttValue **)
+ 			(res->tuples ? realloc(res->tuples, n*sizeof(PGresAttValue *)) :
+ 			 malloc(n*sizeof(PGresAttValue *)));
+ 
+ 		if(!tups)
+ 			return FALSE;
+ 
+ 		memset(tups + res->tupArrSize, 0,
+ 			(n - res->tupArrSize) * sizeof(PGresAttValue *));
+ 		res->tuples = tups;
+ 		res->tupArrSize = n;
+ 	}
+ 
+ 	/* need to allocate a new tuple */
+ 	if(tup_num == res->ntups && !res->tuples[tup_num])
+ 	{
+ 		int i;
+ 		PGresAttValue *tup = (PGresAttValue *)PQresultAlloc(
+ 			res, res->numAttributes * sizeof(PGresAttValue));
+ 
+ 		if(!tup)
+ 			return FALSE;
+ 
+ 		/* initialize each column to NULL */
+ 		for(i=0; i < res->numAttributes; i++)
+ 		{
+ 			tup[i].len = NULL_LEN;
+ 			tup[i].value = res->null_field;
+ 		}
+ 
+ 		res->tuples[tup_num] = tup;
+ 		res->ntups++;
+ 	}
+ 
+ 	attval = &res->tuples[tup_num][field_num];
+ 
+ 	/* On top of NULL_LEN, treat a NULL value as a NULL field */
+ 	if(len == NULL_LEN || value == NULL)
+ 	{
+ 		attval->len = NULL_LEN;
+ 		attval->value = res->null_field;
+ 	}
+ 	else
+ 	{
+ 		if(len < 0)
+ 			len = 0;
+ 
+ 		if(len == 0)
+ 		{
+ 			attval->len = 0;
+ 			attval->value = res->null_field;
+ 		}
+ 		else
+ 		{
+ 			attval->value = (char *)PQresultAlloc(res, len + 1);
+ 			if(!attval->value)
+ 				return FALSE;
+ 
+ 			attval->len = len;
+ 			memcpy(attval->value, value, len);
+ 			attval->value[len] = '\0';
+ 		}
+ 	}
+ 
+ 	return TRUE;
+ }
+ 
+ void *
+ PQresultAlloc(PGresult *res, size_t nBytes)
+ {
+ 	return pqResultAlloc(res, nBytes, TRUE);
+ }
+ 
  /*
   * pqResultAlloc -
   *		Allocate subsidiary storage for a PGresult.
   *
   * nBytes is the amount of space needed for the object.
   * If isBinary is true, we assume that we need to align the object on
***************
*** 349,365 ****
--- 633,664 ----
   * PQclear -
   *	  free's the memory associated with a PGresult
   */
  void
  PQclear(PGresult *res)
  {
+ 	int i;
  	PGresult_data *block;
+ 	PGEventResultDestroy evt;
  
  	if (!res)
  		return;
  
+ 	for(i=0; i < res->nEvents; i++)
+ 	{
+ 		evt.result = res;
+ 		(void)res->events[i].proc(PGEVT_RESULTDESTROY, &evt);
+ 	}
+ 
+ 	if(res->events)
+ 	{
+ 		free(res->events);
+ 		res->events = NULL;
+ 		res->nEvents = 0;
+ 	}
+ 
  	/* Free all the subsidiary blocks */
  	while ((block = res->curBlock) != NULL)
  	{
  		res->curBlock = block->next;
  		free(block);
  	}
***************
*** 1192,1204 ****
   *	  memory).
   */
  
  PGresult *
  PQgetResult(PGconn *conn)
  {
! 	PGresult   *res;
  
  	if (!conn)
  		return NULL;
  
  	/* Parse any available data, if our state permits. */
  	parseInput(conn);
--- 1491,1503 ----
   *	  memory).
   */
  
  PGresult *
  PQgetResult(PGconn *conn)
  {
! 	PGresult   *res=NULL;
  
  	if (!conn)
  		return NULL;
  
  	/* Parse any available data, if our state permits. */
  	parseInput(conn);
***************
*** 1267,1278 ****
--- 1566,1602 ----
  							  libpq_gettext("unexpected asyncStatus: %d\n"),
  							  (int) conn->asyncStatus);
  			res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
  			break;
  	}
  
+ 	if(res && (res->resultStatus == PGRES_COMMAND_OK ||
+ 		 res->resultStatus == PGRES_TUPLES_OK ||
+ 		 res->resultStatus == PGRES_EMPTY_QUERY))
+ 	{
+ 		int i;
+ 		PGEventResultCreate evt;
+ 
+ 		for(i=0; i < res->nEvents; i++)
+ 		{
+ 			evt.conn = conn;
+ 			evt.result = res;
+ 
+ 			if(!res->events[i].proc(PGEVT_RESULTCREATE, &evt))
+ 			{
+ 				char msg[256];
+ 				sprintf(msg,
+ 					"PGEventProc %p failed during PGEVT_RESULTCREATE event",
+ 					res->events[i].proc);
+ 				pqSetResultError(res, msg);
+ 				res->resultStatus = PGRES_FATAL_ERROR;
+ 				break;
+ 			}
+ 		}
+ 	}
+ 
  	return res;
  }
  
  
  /*
   * PQexec
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.142
diff -C6 -r1.142 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h	19 Mar 2008 00:39:33 -0000	1.142
--- src/interfaces/libpq/libpq-fe.h	3 Sep 2008 16:06:50 -0000
***************
*** 25,36 ****
--- 25,45 ----
  /*
   * postgres_ext.h defines the backend's externally visible types,
   * such as Oid.
   */
  #include "postgres_ext.h"
  
+ /* -----------------------
+  * Options for PQcopyResult
+  */
+ 
+ #define PG_COPYRES_ATTRS          0x01
+ #define PG_COPYRES_TUPLES         0x02 /* Implies PG_COPYRES_ATTRS */
+ #define PG_COPYRES_EVENTS         0x04
+ #define PG_COPYRES_NOTICEHOOKS    0x08
+ 
  /* Application-visible enum types */
  
  typedef enum
  {
  	/*
  	 * Although it is okay to add to this list, values which become unused
***************
*** 190,201 ****
--- 199,225 ----
  		int		   *ptr;		/* can't use void (dec compiler barfs)	 */
  		int			integer;
  	}			u;
  } PQArgBlock;
  
  /* ----------------
+  * PGresAttDesc -- Data about a single attribute (column) of a query result
+  * ----------------
+  */
+ typedef struct pgresAttDesc
+ {
+ 	char	   *name;			/* column name */
+ 	Oid			tableid;		/* source table, if known */
+ 	int			columnid;		/* source column, if known */
+ 	int			format;			/* format code for value (text/binary) */
+ 	Oid			typid;			/* type id */
+ 	int			typlen;			/* type size */
+ 	int			atttypmod;  /* type-specific modifier info */
+ } PGresAttDesc;
+ 
+ /* ----------------
   * Exported functions of libpq
   * ----------------
   */
  
  /* ===	in fe-connect.c === */
  
***************
*** 434,445 ****
--- 458,490 ----
   * Make an empty PGresult with given status (some apps find this
   * useful). If conn is not NULL and status indicates an error, the
   * conn's errorMessage is copied.
   */
  extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
  
+ extern PGresult *
+ PQcopyResult(const PGresult *src, int options);
+ 
+ extern int
+ PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);
+ 
+ extern void *
+ PQresultAlloc(PGresult *res, size_t nBytes);
+ 
+ /*
+  * Sets the value for a tuple field.  The tup_num must be less than or
+  * equal to PQntuples(res).  This function will generate tuples as needed.
+  * A new tuple is generated when tup_num equals PQntuples(res) and there
+  * are no fields defined for that tuple.
+  *
+  * Returns a non-zero value for success and zero for failure.
+  */
+ extern int
+ PQsetvalue(PGresult *res, int tup_num, int field_num,
+ 	char *value, int len);
+ 
  
  /* Quoting strings before inclusion in queries. */
  extern size_t PQescapeStringConn(PGconn *conn,
  				   char *to, const char *from, size_t length,
  				   int *error);
  extern unsigned char *PQescapeByteaConn(PGconn *conn,
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.131
diff -C6 -r1.131 libpq-int.h
*** src/interfaces/libpq/libpq-int.h	29 May 2008 22:02:44 -0000	1.131
--- src/interfaces/libpq/libpq-int.h	3 Sep 2008 16:06:50 -0000
***************
*** 19,30 ****
--- 19,31 ----
  
  #ifndef LIBPQ_INT_H
  #define LIBPQ_INT_H
  
  /* We assume libpq-fe.h has already been included. */
  #include "postgres_fe.h"
+ #include "libpq-events.h"
  
  #include <time.h>
  #include <sys/types.h>
  #ifndef WIN32
  #include <sys/time.h>
  #endif
***************
*** 97,121 ****
  union pgresult_data
  {
  	PGresult_data *next;		/* link to next block, or NULL */
  	char		space[1];		/* dummy for accessing block as bytes */
  };
  
- /* Data about a single attribute (column) of a query result */
- 
- typedef struct pgresAttDesc
- {
- 	char	   *name;			/* column name */
- 	Oid			tableid;		/* source table, if known */
- 	int			columnid;		/* source column, if known */
- 	int			format;			/* format code for value (text/binary) */
- 	Oid			typid;			/* type id */
- 	int			typlen;			/* type size */
- 	int			atttypmod;		/* type-specific modifier info */
- } PGresAttDesc;
- 
  /* Data about a single parameter of a prepared statement */
  typedef struct pgresParamDesc
  {
  	Oid			typid;			/* type id */
  } PGresParamDesc;
  
--- 98,109 ----
***************
*** 159,170 ****
--- 147,167 ----
  	PQnoticeReceiver noticeRec; /* notice message receiver */
  	void	   *noticeRecArg;
  	PQnoticeProcessor noticeProc;		/* notice message processor */
  	void	   *noticeProcArg;
  } PGNoticeHooks;
  
+ typedef struct
+ {
+ 	/* pointer supplied by user */
+ 	void *passThrough;
+ 	/* state (instance) data, optionally generated by event proc */
+ 	void *data;
+ 	PGEventProc proc;
+ } PGEvent;
+ 
  struct pg_result
  {
  	int			ntups;
  	int			numAttributes;
  	PGresAttDesc *attDescs;
  	PGresAttValue **tuples;		/* each PGresTuple is an array of
***************
*** 181,192 ****
--- 178,193 ----
  	 * These fields are copied from the originating PGconn, so that operations
  	 * on the PGresult don't have to reference the PGconn.
  	 */
  	PGNoticeHooks noticeHooks;
  	int			client_encoding;	/* encoding id */
  
+ 	/* registered events, copied from conn */
+ 	int nEvents;
+ 	PGEvent *events;
+ 
  	/*
  	 * Error information (all NULL if not an error result).  errMsg is the
  	 * "overall" error message returned by PQresultErrorMessage.  If we have
  	 * per-field info then it is stored in a linked list.
  	 */
  	char	   *errMsg;			/* error message, or NULL if no error */
***************
*** 300,311 ****
--- 301,317 ----
  	/* Optional file to write trace info to */
  	FILE	   *Pfdebug;
  
  	/* Callback procedures for notice message processing */
  	PGNoticeHooks noticeHooks;
  
+ 	/* registered events via PQregisterEventProc */
+ 	int nEvents;
+ 	int eventArrSize;
+ 	PGEvent *events;
+ 
  	/* Status indicators */
  	ConnStatusType status;
  	PGAsyncStatusType asyncStatus;
  	PGTransactionStatusType xactStatus; /* never changes to ACTIVE */
  	PGQueryClass queryclass;
  	char	   *last_query;		/* last SQL command, or NULL if unknown */
Index: src/interfaces/libpq/pthread-win32.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/pthread-win32.c,v
retrieving revision 1.17
diff -C6 -r1.17 pthread-win32.c
*** src/interfaces/libpq/pthread-win32.c	21 May 2008 14:20:48 -0000	1.17
--- src/interfaces/libpq/pthread-win32.c	3 Sep 2008 16:06:50 -0000
***************
*** 2,14 ****
  *
  * pthread-win32.c
  *	 partial pthread implementation for win32
  *
  * Copyright (c) 2004-2008, PostgreSQL Global Development Group
  * IDENTIFICATION
! *	$PostgreSQL: pgsql/src/interfaces/libpq/pthread-win32.c,v 1.17 2008/05/21 14:20:48 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
  
  #include "postgres_fe.h"
  
--- 2,14 ----
  *
  * pthread-win32.c
  *	 partial pthread implementation for win32
  *
  * Copyright (c) 2004-2008, PostgreSQL Global Development Group
  * IDENTIFICATION
! *	$PostgreSQL: pgsql/src/interfaces/libpq/pthread-win32.c,v 1.16 2008/05/16 18:30:53 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
  
  #include "postgres_fe.h"
  


  [application/x-compressed] libpq-events.tgz (8.4K, ../[email protected]/3-libpq-events.tgz)
  download

view thread (63+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected]
  Subject: Re: libpq events patch
  In-Reply-To: <[email protected]>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox