Andrew Chernow escribió:
> Attached is the latest patch.  It has addressed the requested changes 
> found here: 
> http://archives.postgresql.org/pgsql-patches/2008-05/msg00389.php
>
> Its a tarball because there are two new files, libpq-events.c and  
> libpq-events.h.  The patch is in the tarball as well as attached to the 
> email.

I modified this patch slightly.  I was about to try libpqtypes on it,
but then I noticed that libpqtypes as published on pgfoundry is based on
a very old version of this patch, so I punted.  So, for now, the only
guarantee is that it compiles with no warnings.

However, the only change of any significance that I introduced was that 
a "name" is attached to every event proc, so that it can be reported in
error messages, as reporting only %p seems very useless.  (I also
removed PQresultAlloc.)

The API seems reasonable to me.

There's one thing that seems a bit baroque, which is the
PG_COPYRES_USE_ATTRS stuff in PQcopyResult.  I think that flag
introduces different enough behavior that it should be a routine of its
own, say PQcopyResultAttrs.  That way you would leave out the two extra
params in PQcopyResult.

Oh -- one last thing.  I am not really sure about the flags to
PQcopyResult.  Should there really be flags to _remove_ behavior,
instead of flags that add?  i.e. instead of having "0" copy everything,
and have to pass flags for things not to copy, wouldn't it be cleaner to
have 0 copy only base stuff, and require flags to copy extra things?

The main missing thing from this patch is SGML docs for the new libpq
functions.

-- 
Alvaro Herrera                                http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Index: src/interfaces/libpq/Makefile
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/interfaces/libpq/Makefile,v
retrieving revision 1.166
diff -c -p -r1.166 Makefile
*** src/interfaces/libpq/Makefile	16 Apr 2008 14:19:56 -0000	1.166
--- src/interfaces/libpq/Makefile	2 Sep 2008 21:54:47 -0000
*************** LIBS := $(LIBS:-lpgport=)
*** 32,38 ****
  
  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)
--- 32,38 ----
  
  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)
*************** $(top_builddir)/src/port/pg_config_paths
*** 106,111 ****
--- 106,112 ----
  
  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
*** 114,120 ****
  	$(mkinstalldirs) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)'
  
  uninstall: uninstall-lib
! 	rm -f '$(DESTDIR)$(includedir)/libpq-fe.h' '$(DESTDIR)$(includedir_internal)/libpq-int.h' '$(DESTDIR)$(includedir_internal)/pqexpbuffer.h' '$(DESTDIR)$(datadir)/pg_service.conf.sample'
  
  clean distclean: clean-lib
  	rm -f $(OBJS) pg_config_paths.h crypt.c getaddrinfo.c inet_aton.c noblock.c open.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c md5.c ip.c encnames.c wchar.c win32error.c pgsleep.c pthread.h libpq.rc
--- 115,121 ----
  	$(mkinstalldirs) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)'
  
  uninstall: uninstall-lib
! 	rm -f '$(DESTDIR)$(includedir)/libpq-fe.h' '$(DESTDIR)$(includedir)/libpq-events.h' '$(DESTDIR)$(includedir_internal)/libpq-int.h' '$(DESTDIR)$(includedir_internal)/pqexpbuffer.h' '$(DESTDIR)$(datadir)/pg_service.conf.sample'
  
  clean distclean: clean-lib
  	rm -f $(OBJS) pg_config_paths.h crypt.c getaddrinfo.c inet_aton.c noblock.c open.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c md5.c ip.c encnames.c wchar.c win32error.c pgsleep.c pthread.h libpq.rc
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.19
diff -c -p -r1.19 exports.txt
*** src/interfaces/libpq/exports.txt	19 Mar 2008 00:39:33 -0000	1.19
--- src/interfaces/libpq/exports.txt	2 Sep 2008 22:41:17 -0000
*************** PQconnectionUsedPassword  138
*** 141,143 ****
--- 141,152 ----
  pg_valid_server_encoding_id 139
  PQconnectionNeedsPassword 140
  lo_import_with_oid		  141
+ PQcopyResult              142
+ PQsetvalue                143
+ PQregisterEventProc       144
+ PQinstanceData            145
+ PQsetInstanceData         146
+ PQresultInstanceData      147
+ PQresultSetInstanceData   148
+ PQpassThroughData         149
+ PQresultPassThroughData   150
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.359
diff -c -p -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	2 Sep 2008 23:22:02 -0000
*************** makeEmptyPGconn(void)
*** 1974,1979 ****
--- 1974,1998 ----
  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(conn->events[i].name);
+ 	}
+ 
+ 	/* 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)
*************** PQreset(PGconn *conn)
*** 2155,2162 ****
  	{
  		closePGconn(conn);
  
! 		if (connectDBStart(conn))
! 			(void) connectDBComplete(conn);
  	}
  }
  
--- 2174,2198 ----
  	{
  		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("PGEvent \"%s\" failed during PGEVT_CONNRESET event\n"),
! 						conn->events[i].name);
! 					break;
! 				}
! 			}
! 		}
  	}
  }
  
*************** PostgresPollingStatusType
*** 2190,2196 ****
  PQresetPoll(PGconn *conn)
  {
  	if (conn)
! 		return PQconnectPoll(conn);
  
  	return PGRES_POLLING_FAILED;
  }
--- 2226,2256 ----
  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("PGEvent \"%s\" failed during PGEVT_CONNRESET event\n"),
! 						conn->events[i].name);
! 					return PGRES_POLLING_FAILED;
! 				}
! 			}
! 		}
! 
! 		return status;
! 	}
  
  	return PGRES_POLLING_FAILED;
  }
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.196
diff -c -p -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	2 Sep 2008 23:21:12 -0000
*************** static bool PQexecStart(PGconn *conn);
*** 63,68 ****
--- 63,69 ----
  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);
  
  
  /* ----------------
*************** static int PQsendDescribe(PGconn *conn, 
*** 125,134 ****
  
  
  /*
   * 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
--- 126,163 ----
  
  
  /*
+  * Creates a copy of a PGEvent array.  The event proc name is not allocated
+  * separately.
+  *
+  * Note that it doesn't duplicate the event instance data; it is left 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 and events state are 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
*************** PQmakeEmptyPGresult(PGconn *conn, ExecSt
*** 160,165 ****
--- 189,196 ----
  	result->curBlock = NULL;
  	result->curOffset = 0;
  	result->spaceLeft = 0;
+ 	result->nEvents = 0;
+ 	result->events = NULL;
  
  	if (conn)
  	{
*************** PQmakeEmptyPGresult(PGconn *conn, ExecSt
*** 167,172 ****
--- 198,216 ----
  		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)
  		{
*************** PQmakeEmptyPGresult(PGconn *conn, ExecSt
*** 196,201 ****
--- 240,477 ----
  }
  
  /*
+  * 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.  If this value is 0, the entire result is deep copied.
+  * The created result is always put into the PGRES_TUPLES_OK status.  The
+  * source result error message is not copied, although cmdStatus is.
+  *
+  * Options:
+  *   PG_COPYRES_NO_TUPLES - Do not copy the tuples.  This option is
+  *   automatically enabled when PG_COPYRES_USE_ATTRS is set.
+  *
+  *   PG_COPYRES_USE_ATTRS - Indicates that the 'numAttributes' and 'attDescs'
+  *   arguments should be used as the fields for the created result, rather
+  *   than copying them from the source result.  When this option is set,
+  *   the tuples will NOT be copied, behaving identically to setting the
+  *   PG_COPYRES_NO_TUPLES option.  One must use PQsetvalue to manually
+  *   add tuples to the returned result.  NOTE: numAttributes and attDescs
+  *   arguments are ignored unless this option is set!
+  *
+  *   PG_COPYRES_NO_EVENTS - Indicates that the source result's
+  *   events should NOT be copied to the created result.
+  *
+  *   PG_COPYRES_NO_NOTICEHOOKS - Indicates that the source result's
+  *   NoticeHooks should NOT be copied to the created result.
+  */
+ PGresult *
+ PQcopyResult(const PGresult *src, int numAttributes,
+ 	PGresAttDesc *attDescs, int options)
+ {
+ 	int i;
+ 	PGresult *dest;
+ 	PGEventResultCopy evt;
+ 
+ 	if (!src)
+ 		return NULL;
+ 
+ 	/* Automatically turn on no_tuples since use_attrs is set.  It makes
+ 	 * no sense to copy tuples into an unknown set of columns.
+ 	 */
+ 	if (options & PG_COPYRES_USE_ATTRS)
+ 		options |= PG_COPYRES_NO_TUPLES;
+ 
+ 	/* If use_attrs is set, verify attr arguments. */
+ 	if ((options & PG_COPYRES_USE_ATTRS) && (numAttributes <= 0 || !attDescs))
+ 		return NULL;
+ 
+ 	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_NO_NOTICEHOOKS))
+ 		dest->noticeHooks = src->noticeHooks;
+ 
+ 	/* Copy from src result when not supplying attrs */
+ 	if (!(options & PG_COPYRES_USE_ATTRS) && src->numAttributes > 0)
+ 	{
+ 		numAttributes = src->numAttributes;
+ 		attDescs = src->attDescs;
+ 	}
+ 
+ 	/* copy attrs */
+ 	if (numAttributes > 0)
+ 	{
+ 		dest->attDescs = (PGresAttDesc *) pqResultAlloc(dest,
+ 			numAttributes * sizeof(PGresAttDesc), TRUE);
+ 
+ 		if (!dest->attDescs)
+ 		{
+ 			PQclear(dest);
+ 			return NULL;
+ 		}
+ 
+ 		dest->numAttributes = numAttributes;
+ 		memcpy(dest->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).
+ 		 */
+ 		dest->binary = 1;
+ 		for (i = 0; i < numAttributes; i++)
+ 		{
+ 			if (attDescs[i].name)
+ 				dest->attDescs[i].name = pqResultStrdup(dest, attDescs[i].name);
+ 			else
+ 				dest->attDescs[i].name = dest->null_field;
+ 
+ 			if (!dest->attDescs[i].name)
+ 			{
+ 				PQclear(dest);
+ 				return NULL;
+ 			}
+ 
+ 			/* Although deprecated, because results can have text+binary columns,
+ 			 * its easy enough to deduce so set it for completeness.
+ 			 */
+ 			if (dest->attDescs[i].format == 0)
+ 				dest->binary = 0;
+ 		}
+ 	}
+ 
+ 	/* Wants to copy result tuples: use PQsetvalue(). */
+ 	if (!(options & PG_COPYRES_NO_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_NO_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 * 2 : 128;
+ 		PGresAttValue **tups;
+ 
+ 		if (res->tuples)
+ 			tups = realloc(res->tuples, n * sizeof(PGresAttValue *));
+ 		else
+ 			tups = 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;
+ 		
+ 		tup = (PGresAttValue *) pqResultAlloc(res, res->numAttributes * sizeof(PGresAttValue), TRUE);
+ 
+ 		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, TRUE);
+ 			if (!attval->value)
+ 				return FALSE;
+ 
+ 			attval->len = len;
+ 			memcpy(attval->value, value, len);
+ 			attval->value[len] = '\0';
+ 		}
+ 	}
+ 
+ 	return TRUE;
+ }
+ 
+ /*
   * pqResultAlloc -
   *		Allocate subsidiary storage for a PGresult.
   *
*************** pqCatenateResultError(PGresult *res, con
*** 352,362 ****
--- 628,655 ----
  void
  PQclear(PGresult *res)
  {
+ 	int i;
  	PGresult_data *block;
+ 	PGEventResultDestroy evt;
  
  	if (!res)
  		return;
  
+ 	/* invoke each event's destroy routine */
+ 	for (i = 0; i < res->nEvents; i++)
+ 	{
+ 		evt.result = res;
+ 		(void) res->events[i].proc(PGEVT_RESULTDESTROY, &evt);
+ 	}
+ 
+ 	/* free the event array */
+ 	if (res->events)
+ 	{
+ 		free(res->events);
+ 		res->events = NULL;
+ 		res->nEvents = 0;
+ 	}
+ 
  	/* Free all the subsidiary blocks */
  	while ((block = res->curBlock) != NULL)
  	{
*************** PQgetResult(PGconn *conn)
*** 1270,1275 ****
--- 1563,1594 ----
  			break;
  	}
  
+ 	if (res && res->nEvents > 0 &&
+ 		(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,
+ 					"PGEvent \"%s\" failed during PGEVT_RESULTCREATE event",
+ 					res->events[i].name);
+ 				pqSetResultError(res, msg);
+ 				res->resultStatus = PGRES_FATAL_ERROR;
+ 				break;
+ 			}
+ 		}
+ 	}
+ 
  	return res;
  }
  
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.142
diff -c -p -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	2 Sep 2008 23:23:48 -0000
*************** extern		"C"
*** 28,33 ****
--- 28,42 ----
   */
  #include "postgres_ext.h"
  
+ /* -----------------------
+  * Options for PQcopyResult
+  */
+ 
+ #define PG_COPYRES_NO_TUPLES      0x01
+ #define PG_COPYRES_USE_ATTRS      0x02
+ #define PG_COPYRES_NO_EVENTS      0x04
+ #define PG_COPYRES_NO_NOTICEHOOKS 0x08
+ 
  /* Application-visible enum types */
  
  typedef enum
*************** typedef struct
*** 193,198 ****
--- 202,222 ----
  } 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
   * ----------------
   */
*************** extern void PQfreemem(void *ptr);
*** 437,442 ****
--- 461,481 ----
   */
  extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
  
+ extern PGresult *
+ PQcopyResult(const PGresult *src, int numAttributes,
+ 	PGresAttDesc *attDescs, int options);
+ 
+ /*
+  * 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,
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.131
diff -c -p -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	2 Sep 2008 23:24:39 -0000
***************
*** 22,27 ****
--- 22,28 ----
  
  /* We assume libpq-fe.h has already been included. */
  #include "postgres_fe.h"
+ #include "libpq-events.h"
  
  #include <time.h>
  #include <sys/types.h>
*************** union pgresult_data
*** 100,118 ****
  	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
  {
--- 101,106 ----
*************** typedef struct
*** 162,167 ****
--- 150,164 ----
  	void	   *noticeProcArg;
  } PGNoticeHooks;
  
+ /* Fields needed for PGEvent processing */
+ typedef struct
+ {
+ 	char	   *name;	/* for error messages */
+ 	void	   *passThrough; /* pointer supplied by user */
+ 	void	   *data;	/* state data, optionally generated by event proc */
+ 	PGEventProc	proc;	/* the function to call on events */
+ } PGEvent;
+ 
  struct pg_result
  {
  	int			ntups;
*************** struct pg_result
*** 184,189 ****
--- 181,190 ----
  	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
*************** struct pg_conn
*** 303,308 ****
--- 304,314 ----
  	/* Callback procedures for notice message processing */
  	PGNoticeHooks noticeHooks;
  
+ 	/* registered events via PQregisterEventProc */
+ 	int nEvents;
+ 	int eventArrSize;
+ 	PGEvent *events;
+ 
  	/* Status indicators */
  	ConnStatusType status;
  	PGAsyncStatusType asyncStatus;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to