Re: libpq object hooks (libpq events)

Andrew Chernow <[email protected]>
Newsgroups gmane.comp.db.postgresql.devel.patches
Message-ID <[email protected]>
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.

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


-- 
Sent via pgsql-patches mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-patches
libpq-events.tgz (application/x-compressed, 8.3 KB) - not displayed
libpq-events.patch (text/x-patch, 24.7 KB)
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	20 May 2008 04:18:07 -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	20 May 2008 04:18:07 -0000
***************
*** 138,143 ****
--- 138,153 ----
  PQsendDescribePortal      136
  lo_truncate               137
  PQconnectionUsedPassword  138
  pg_valid_server_encoding_id 139
  PQconnectionNeedsPassword 140
  lo_import_with_oid		  141
+ PQcopyResult              142
+ PQsetvalue                143
+ PQresultAlloc             144
+ PQregisterEventProc       145
+ PQinstanceData            146
+ PQsetInstanceData         147
+ PQresultInstanceData      148
+ PQresultSetInstanceData   149
+ PQpassThroughData         150
+ PQresultPassThroughData   151
\ No newline at end of file
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.358
diff -C6 -r1.358 fe-connect.c
*** src/interfaces/libpq/fe-connect.c	16 May 2008 18:30:53 -0000	1.358
--- src/interfaces/libpq/fe-connect.c	20 May 2008 04:18:08 -0000
***************
*** 1970,1981 ****
--- 1970,1999 ----
   * 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);
***************
*** 2151,2164 ****
  PQreset(PGconn *conn)
  {
  	if (conn)
  	{
  		closePGconn(conn);
  
! 		if (connectDBStart(conn))
! 			(void) connectDBComplete(conn);
  	}
  }
  
  
  /*
   * PQresetStart:
--- 2169,2199 ----
  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:
***************
*** 2186,2198 ****
   * 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.
--- 2221,2257 ----
   * 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.194
diff -C6 -r1.194 fe-exec.c
*** src/interfaces/libpq/fe-exec.c	1 Jan 2008 19:46:00 -0000	1.194
--- src/interfaces/libpq/fe-exec.c	20 May 2008 04:18:08 -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:
***************
*** 193,204 ****
--- 234,481 ----
  	}
  
  	return result;
  }
  
  /*
+  * 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));
+ 
+ 		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*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
   * a machine allocation boundary.
***************
*** 349,365 ****
--- 626,657 ----
   * 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);
  	}
***************
*** 1190,1202 ****
   *	  memory).
   */
  
  PGresult *
  PQgetResult(PGconn *conn)
  {
! 	PGresult   *res;
  
  	if (!conn)
  		return NULL;
  
  	/* Parse any available data, if our state permits. */
  	parseInput(conn);
--- 1482,1494 ----
   *	  memory).
   */
  
  PGresult *
  PQgetResult(PGconn *conn)
  {
! 	PGresult   *res=NULL;
  
  	if (!conn)
  		return NULL;
  
  	/* Parse any available data, if our state permits. */
  	parseInput(conn);
***************
*** 1265,1276 ****
--- 1557,1593 ----
  							  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/fe-misc.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v
retrieving revision 1.133
diff -C6 -r1.133 fe-misc.c
*** src/interfaces/libpq/fe-misc.c	1 Jan 2008 19:46:00 -0000	1.133
--- src/interfaces/libpq/fe-misc.c	20 May 2008 04:18:09 -0000
***************
*** 1152,1157 ****
--- 1152,1158 ----
  	}
  
  	return dgettext("libpq", msgid);
  }
  
  #endif   /* ENABLE_NLS */
+ 
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	20 May 2008 04:18:09 -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_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
  {
  	/*
  	 * 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,488 ----
   * 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 numAttributes,
+ 	PGresAttDesc *attDescs, int options);
+ 
+ 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.130
diff -C6 -r1.130 libpq-int.h
*** src/interfaces/libpq/libpq-int.h	16 May 2008 18:30:53 -0000	1.130
--- src/interfaces/libpq/libpq-int.h	20 May 2008 04:18:09 -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 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 */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.