Skip site navigation (1) Skip section navigation (2)

libpq cleanup for SIGPIPE and stderr concerns

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: pgsql-interfaces(at)postgreSQL(dot)org, pgsql-patches(at)postgreSQL(dot)org
Subject: libpq cleanup for SIGPIPE and stderr concerns
Date: 1998-08-09 00:35:55
Message-ID: 3234.902622955@sss.pgh.pa.us (view raw or flat)
Thread:
Lists: pgsql-interfaces
The attached patch implements some changes that were discussed a couple
weeks ago on the hackers and interfaces lists:
1. When the backend sends a NOTICE message and closes the connection
   (typically, because it was told to by the postmaster after another
   backend coredumped), libpq will now print the notice and close the
   connection cleanly.  Formerly, the frontend app would usually
   terminate ungracefully due to a SIGPIPE.  (I am not sure if 6.3.2
   behaved that way, but the current cvs sources do...)
2. libpq's various printouts to stderr are now fed through a single
   "notice processor" routine, which can be overridden by the
   application to direct notices someplace else.  This should ease
   porting libpq to Windows.

I also noticed and fixed a problem in PQprint: when sending output
to a pager subprocess, it would disable SIGPIPE in case the pager
terminates early (this is good) --- but afterwards it reset SIGPIPE to
SIG_DFL, rather than restoring the application's prior setting (bad).

			regards, tom lane


*** src/interfaces/libpq/libpq-fe.h.orig	Sat Jul 18 14:34:34 1998
--- src/interfaces/libpq/libpq-fe.h	Sat Aug  8 19:17:03 1998
***************
*** 121,126 ****
--- 121,129 ----
  		int			be_pid;		/* process id of backend */
  	} PGnotify;
  
+ /* PQnoticeProcessor is a typedef for a callback function type */
+ 	typedef void (*PQnoticeProcessor) (void * arg, const char * message);
+ 
  /* PGAsyncStatusType is private to libpq, really shouldn't be seen by users */
  	typedef enum
  	{
***************
*** 165,170 ****
--- 168,177 ----
  		/* Optional file to write trace info to */
  		FILE	   *Pfdebug;
  
+ 		/* Callback procedure for notice/error message processing */
+ 		PQnoticeProcessor	noticeHook;
+ 		void	   *noticeArg;
+ 
  		/* Status indicators */
  		ConnStatusType		status;
  		PGAsyncStatusType	asyncStatus;
***************
*** 300,305 ****
--- 307,317 ----
  	extern void PQtrace(PGconn *conn, FILE *debug_port);
  	extern void PQuntrace(PGconn *conn);
  
+ 	/* Override default notice processor */
+ 	extern void PQsetNoticeProcessor (PGconn *conn,
+ 									  PQnoticeProcessor proc,
+ 									  void *arg);
+ 
  /* === in fe-exec.c === */
  	/* Simple synchronous query */
  	extern PGresult *PQexec(PGconn *conn, const char *query);
***************
*** 390,395 ****
--- 402,408 ----
  	extern int	pqGetInt(int *result, int bytes, PGconn *conn);
  	extern int	pqPutInt(int value, int bytes, PGconn *conn);
  	extern int	pqReadData(PGconn *conn);
+ 	extern int	pqReadReady(PGconn *conn);
  	extern int	pqFlush(PGconn *conn);
  	extern int	pqWait(int forRead, int forWrite, PGconn *conn);
  
*** src/interfaces/libpq/fe-auth.c.orig	Sat Aug  1 15:56:30 1998
--- src/interfaces/libpq/fe-auth.c	Sat Aug  8 19:56:36 1998
***************
*** 445,452 ****
  			(void) sprintf(PQerrormsg,
  				  "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
  						   error->text.length, error->text.data);
- 			fputs(PQerrormsg, stderr);
- 			pqdebug("%s", PQerrormsg);
  		}
  		else
  		{
--- 445,450 ----
*** src/interfaces/libpq/fe-print.c.orig	Sat Aug  1 15:56:30 1998
--- src/interfaces/libpq/fe-print.c	Sat Aug  8 18:48:54 1998
***************
*** 106,111 ****
--- 106,112 ----
  		int			fs_len = strlen(po->fieldSep);
  		int			total_line_length = 0;
  		int			usePipe = 0;
+ 		pqsigfunc	oldsigpipehandler = NULL;
  		char	   *pagerenv;
  		char		buf[8192 * 2 + 1];
  
***************
*** 193,199 ****
  				{
  					usePipe = 1;
  #ifndef WIN32
! 					pqsignal(SIGPIPE, SIG_IGN);
  #endif
  				}
  				else
--- 194,200 ----
  				{
  					usePipe = 1;
  #ifndef WIN32
! 					oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
  #endif
  				}
  				else
***************
*** 309,315 ****
  			_pclose(fout);
  #else
  			pclose(fout);
! 			pqsignal(SIGPIPE, SIG_DFL);
  #endif
  		}
  		if (po->html3 && !po->expanded)
--- 310,316 ----
  			_pclose(fout);
  #else
  			pclose(fout);
! 			pqsignal(SIGPIPE, oldsigpipehandler);
  #endif
  		}
  		if (po->html3 && !po->expanded)
*** src/interfaces/libpq/fe-exec.c.orig	Mon Jul 13 22:41:25 1998
--- src/interfaces/libpq/fe-exec.c	Sat Aug  8 19:57:05 1998
***************
*** 44,49 ****
--- 44,53 ----
  };
  
  
+ #define DONOTICE(conn,message) \
+ 	((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
+ 
+ 
  static PGresult *makeEmptyPGresult(PGconn *conn, ExecStatusType status);
  static void freeTuple(PGresAttValue *tuple, int numAttributes);
  static void addTuple(PGresult *res, PGresAttValue *tup);
***************
*** 198,203 ****
--- 202,215 ----
  		sprintf(conn->errorMessage, "PQsendQuery() -- query pointer is null.");
  		return 0;
  	}
+ 	/* check to see if the query string is too long */
+ 	if (strlen(query) > MAX_MESSAGE_LEN-2)
+ 	{
+ 		sprintf(conn->errorMessage, "PQsendQuery() -- query is too long.  "
+ 				"Maximum length is %d\n", MAX_MESSAGE_LEN - 2);
+ 		return 0;
+ 	}
+ 
  	if (conn->asyncStatus != PGASYNC_IDLE)
  	{
  		sprintf(conn->errorMessage,
***************
*** 205,224 ****
  		return 0;
  	}
  
! 	/* clear the error string */
! 	conn->errorMessage[0] = '\0';
! 
! 	/* initialize async result-accumulation state */
! 	conn->result = NULL;
! 	conn->curTuple = NULL;
! 	conn->asyncErrorMessage[0] = '\0';
! 
! 	/* check to see if the query string is too long */
! 	if (strlen(query) > MAX_MESSAGE_LEN-2)
! 	{
! 		sprintf(conn->errorMessage, "PQsendQuery() -- query is too long.  "
! 				"Maximum length is %d\n", MAX_MESSAGE_LEN - 2);
! 		return 0;
  	}
  
  	/* Don't try to send if we know there's no live connection. */
--- 217,232 ----
  		return 0;
  	}
  
! 	/* Check for pending input (asynchronous Notice or Notify messages);
! 	 * also detect the case that the backend just closed the connection.
! 	 * Note: we have to loop if the first call to pqReadData successfully
! 	 * reads some data, since in that case pqReadData won't notice whether
! 	 * the connection is now closed.
! 	 */
! 	while (pqReadReady(conn)) {
! 		if (pqReadData(conn) < 0)
! 			return 0;			/* errorMessage already set */
! 		parseInput(conn);		/* deal with Notice or Notify, if any */
  	}
  
  	/* Don't try to send if we know there's no live connection. */
***************
*** 229,234 ****
--- 237,250 ----
  		return 0;
  	}
  
+ 	/* clear the error string */
+ 	conn->errorMessage[0] = '\0';
+ 
+ 	/* initialize async result-accumulation state */
+ 	conn->result = NULL;
+ 	conn->curTuple = NULL;
+ 	conn->asyncErrorMessage[0] = '\0';
+ 
  	/* send the query to the backend; */
  	/* the frontend-backend protocol uses 'Q' to designate queries */
  	if (pqPutnchar("Q", 1, conn))
***************
*** 297,311 ****
  		if (pqGetc(&id, conn))
  			return;
  		/*
! 		 * NOTIFY messages can happen in any state besides COPY OUT;
  		 * always process them right away.
  		 */
  		if (id == 'A')
  		{
- 			/* Notify responses can happen at any time */
  			if (getNotify(conn))
  				return;
  		}
  		else
  		{
  			/*
--- 313,331 ----
  		if (pqGetc(&id, conn))
  			return;
  		/*
! 		 * NOTIFY and NOTICE messages can happen in any state besides COPY OUT;
  		 * always process them right away.
  		 */
  		if (id == 'A')
  		{
  			if (getNotify(conn))
  				return;
  		}
+ 		else if (id == 'N')
+ 		{
+ 			if (getNotice(conn))
+ 				return;
+ 		}
  		else
  		{
  			/*
***************
*** 318,326 ****
  			{
  				if (conn->asyncStatus == PGASYNC_IDLE)
  				{
! 					fprintf(stderr,
  							"Backend message type 0x%02x arrived while idle\n",
  							id);
  					/* Discard the unexpected message; good idea?? */
  					conn->inStart = conn->inEnd;
  				}
--- 338,347 ----
  			{
  				if (conn->asyncStatus == PGASYNC_IDLE)
  				{
! 					sprintf(conn->errorMessage,
  							"Backend message type 0x%02x arrived while idle\n",
  							id);
+ 					DONOTICE(conn, conn->errorMessage);
  					/* Discard the unexpected message; good idea?? */
  					conn->inStart = conn->inEnd;
  				}
***************
*** 354,361 ****
  					if (pqGetc(&id, conn))
  						return;
  					if (id != '\0')
! 						fprintf(stderr,
  								"unexpected character %c following 'I'\n", id);
  					if (conn->result == NULL)
  						conn->result = makeEmptyPGresult(conn,
  														 PGRES_EMPTY_QUERY);
--- 375,385 ----
  					if (pqGetc(&id, conn))
  						return;
  					if (id != '\0')
! 					{
! 						sprintf(conn->errorMessage,
  								"unexpected character %c following 'I'\n", id);
+ 						DONOTICE(conn, conn->errorMessage);
+ 					}
  					if (conn->result == NULL)
  						conn->result = makeEmptyPGresult(conn,
  														 PGRES_EMPTY_QUERY);
***************
*** 371,380 ****
  					if (pqGetInt(&(conn->be_key), 4, conn))
  						return;
  					break;
- 				case 'N':		/* notices from the backend */
- 					if (getNotice(conn))
- 						return;
- 					break;
  				case 'P':		/* synchronous (normal) portal */
  					if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
  						return;
--- 395,400 ----
***************
*** 408,415 ****
  					}
  					else
  					{
! 						fprintf(stderr,
  								"Backend sent D message without prior T\n");
  						/* Discard the unexpected message; good idea?? */
  						conn->inStart = conn->inEnd;
  						return;
--- 428,436 ----
  					}
  					else
  					{
! 						sprintf(conn->errorMessage,
  								"Backend sent D message without prior T\n");
+ 						DONOTICE(conn, conn->errorMessage);
  						/* Discard the unexpected message; good idea?? */
  						conn->inStart = conn->inEnd;
  						return;
***************
*** 424,431 ****
  					}
  					else
  					{
! 						fprintf(stderr,
  								"Backend sent B message without prior T\n");
  						/* Discard the unexpected message; good idea?? */
  						conn->inStart = conn->inEnd;
  						return;
--- 445,453 ----
  					}
  					else
  					{
! 						sprintf(conn->errorMessage,
  								"Backend sent B message without prior T\n");
+ 						DONOTICE(conn, conn->errorMessage);
  						/* Discard the unexpected message; good idea?? */
  						conn->inStart = conn->inEnd;
  						return;
***************
*** 783,796 ****
  {
  	if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
  		return EOF;
! 	/*
! 	 * Should we really be doing this?	These notices
! 	 * are not important enough for us to presume to
! 	 * put them on stderr.	Maybe the caller should
! 	 * decide whether to put them on stderr or not.
! 	 * BJH 96.12.27
! 	 */
! 	fprintf(stderr, "%s", conn->errorMessage);
  	return 0;
  }
  
--- 805,811 ----
  {
  	if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
  		return EOF;
! 	DONOTICE(conn, conn->errorMessage);
  	return 0;
  }
  
***************
*** 970,976 ****
  	 * To recover, reset the connection (talk about using a sledgehammer...)
  	 */
  	PQclear(result);
! 	fprintf(stderr, "PQendcopy: resetting connection\n");
  	PQreset(conn);
  
  	return 1;
--- 985,994 ----
  	 * To recover, reset the connection (talk about using a sledgehammer...)
  	 */
  	PQclear(result);
! 
! 	sprintf(conn->errorMessage, "PQendcopy: resetting connection\n");
! 	DONOTICE(conn, conn->errorMessage);
! 
  	PQreset(conn);
  
  	return 1;
***************
*** 1156,1166 ****
  PQresultStatus(PGresult *res)
  {
  	if (!res)
- 	{
- 		fprintf(stderr, "PQresultStatus() -- pointer to PQresult is null\n");
  		return PGRES_NONFATAL_ERROR;
- 	}
- 
  	return res->resultStatus;
  }
  
--- 1174,1180 ----
***************
*** 1168,1177 ****
  PQntuples(PGresult *res)
  {
  	if (!res)
- 	{
- 		fprintf(stderr, "PQntuples() -- pointer to PQresult is null\n");
  		return 0;
- 	}
  	return res->ntups;
  }
  
--- 1182,1188 ----
***************
*** 1179,1210 ****
  PQnfields(PGresult *res)
  {
  	if (!res)
- 	{
- 		fprintf(stderr, "PQnfields() -- pointer to PQresult is null\n");
  		return 0;
- 	}
  	return res->numAttributes;
  }
  
  /*
!    returns NULL if the field_num is invalid
! */
! char *
! PQfname(PGresult *res, int field_num)
  {
  	if (!res)
  	{
! 		fprintf(stderr, "PQfname() -- pointer to PQresult is null\n");
! 		return NULL;
  	}
  
  	if (field_num < 0 || field_num >= res->numAttributes)
  	{
! 		fprintf(stderr,
! 				"PQfname: ERROR! field number %d is out of range 0..%d\n",
! 				field_num, res->numAttributes - 1);
! 		return NULL;
  	}
  	if (res->attDescs)
  		return res->attDescs[field_num].name;
  	else
--- 1190,1253 ----
  PQnfields(PGresult *res)
  {
  	if (!res)
  		return 0;
  	return res->numAttributes;
  }
  
  /*
!  * Helper routines to range-check field numbers and tuple numbers.
!  * Return TRUE if OK, FALSE if not
!  */
! 
! static int
! check_field_number(const char *routineName, PGresult *res, int field_num)
  {
  	if (!res)
+ 		return FALSE;			/* no way to display error message... */
+ 	if (field_num < 0 || field_num >= res->numAttributes)
  	{
! 		sprintf(res->conn->errorMessage,
! 				"%s: ERROR! field number %d is out of range 0..%d\n",
! 				routineName, field_num, res->numAttributes - 1);
! 		DONOTICE(res->conn, res->conn->errorMessage);
! 		return FALSE;
  	}
+ 	return TRUE;
+ }
  
+ static int
+ check_tuple_field_number(const char *routineName, PGresult *res,
+ 						 int tup_num, int field_num)
+ {
+ 	if (!res)
+ 		return FALSE;			/* no way to display error message... */
+ 	if (tup_num < 0 || tup_num >= res->ntups)
+ 	{
+ 		sprintf(res->conn->errorMessage,
+ 				"%s: ERROR! tuple number %d is out of range 0..%d\n",
+ 				routineName, tup_num, res->ntups - 1);
+ 		DONOTICE(res->conn, res->conn->errorMessage);
+ 		return FALSE;
+ 	}
  	if (field_num < 0 || field_num >= res->numAttributes)
  	{
! 		sprintf(res->conn->errorMessage,
! 				"%s: ERROR! field number %d is out of range 0..%d\n",
! 				routineName, field_num, res->numAttributes - 1);
! 		DONOTICE(res->conn, res->conn->errorMessage);
! 		return FALSE;
  	}
+ 	return TRUE;
+ }
+ 
+ /*
+    returns NULL if the field_num is invalid
+ */
+ char *
+ PQfname(PGresult *res, int field_num)
+ {
+ 	if (! check_field_number("PQfname", res, field_num))
+ 		return NULL;
  	if (res->attDescs)
  		return res->attDescs[field_num].name;
  	else
***************
*** 1221,1230 ****
  	char	   *field_case;
  
  	if (!res)
- 	{
- 		fprintf(stderr, "PQfnumber() -- pointer to PQresult is null\n");
  		return -1;
- 	}
  
  	if (field_name == NULL ||
  		field_name[0] == '\0' ||
--- 1264,1270 ----
***************
*** 1258,1276 ****
  Oid
  PQftype(PGresult *res, int field_num)
  {
! 	if (!res)
! 	{
! 		fprintf(stderr, "PQftype() -- pointer to PQresult is null\n");
  		return InvalidOid;
- 	}
- 
- 	if (field_num < 0 || field_num >= res->numAttributes)
- 	{
- 		fprintf(stderr,
- 				"PQftype: ERROR! field number %d is out of range 0..%d\n",
- 				field_num, res->numAttributes - 1);
- 		return InvalidOid;
- 	}
  	if (res->attDescs)
  		return res->attDescs[field_num].typid;
  	else
--- 1298,1305 ----
  Oid
  PQftype(PGresult *res, int field_num)
  {
! 	if (! check_field_number("PQftype", res, field_num))
  		return InvalidOid;
  	if (res->attDescs)
  		return res->attDescs[field_num].typid;
  	else
***************
*** 1280,1298 ****
  short
  PQfsize(PGresult *res, int field_num)
  {
! 	if (!res)
! 	{
! 		fprintf(stderr, "PQfsize() -- pointer to PQresult is null\n");
! 		return 0;
! 	}
! 
! 	if (field_num < 0 || field_num >= res->numAttributes)
! 	{
! 		fprintf(stderr,
! 				"PQfsize: ERROR! field number %d is out of range 0..%d\n",
! 				field_num, res->numAttributes - 1);
  		return 0;
- 	}
  	if (res->attDescs)
  		return res->attDescs[field_num].typlen;
  	else
--- 1309,1316 ----
  short
  PQfsize(PGresult *res, int field_num)
  {
! 	if (! check_field_number("PQfsize", res, field_num))
  		return 0;
  	if (res->attDescs)
  		return res->attDescs[field_num].typlen;
  	else
***************
*** 1302,1320 ****
  int
  PQfmod(PGresult *res, int field_num)
  {
! 	if (!res)
! 	{
! 		fprintf(stderr, "PQfmod() -- pointer to PQresult is null\n");
! 		return 0;
! 	}
! 
! 	if (field_num < 0 || field_num >= res->numAttributes)
! 	{
! 		fprintf(stderr,
! 				"PQfmod: ERROR! field number %d is out of range 0..%d\n",
! 				field_num, res->numAttributes - 1);
  		return 0;
- 	}
  	if (res->attDescs)
  		return res->attDescs[field_num].atttypmod;
  	else
--- 1320,1327 ----
  int
  PQfmod(PGresult *res, int field_num)
  {
! 	if (! check_field_number("PQfmod", res, field_num))
  		return 0;
  	if (res->attDescs)
  		return res->attDescs[field_num].atttypmod;
  	else
***************
*** 1325,1334 ****
  PQcmdStatus(PGresult *res)
  {
  	if (!res)
- 	{
- 		fprintf(stderr, "PQcmdStatus() -- pointer to PQresult is null\n");
  		return NULL;
- 	}
  	return res->cmdStatus;
  }
  
--- 1332,1338 ----
***************
*** 1343,1352 ****
  	static char oidStatus[32] = {0};
  
  	if (!res)
! 	{
! 		fprintf(stderr, "PQoidStatus () -- pointer to PQresult is null\n");
! 		return NULL;
! 	}
  
  	oidStatus[0] = 0;
  
--- 1347,1353 ----
  	static char oidStatus[32] = {0};
  
  	if (!res)
! 		return "";
  
  	oidStatus[0] = 0;
  
***************
*** 1371,1380 ****
  PQcmdTuples(PGresult *res)
  {
  	if (!res)
! 	{
! 		fprintf(stderr, "PQcmdTuples () -- pointer to PQresult is null\n");
! 		return NULL;
! 	}
  
  	if (strncmp(res->cmdStatus, "INSERT", 6) == 0 ||
  		strncmp(res->cmdStatus, "DELETE", 6) == 0 ||
--- 1372,1378 ----
  PQcmdTuples(PGresult *res)
  {
  	if (!res)
! 		return "";
  
  	if (strncmp(res->cmdStatus, "INSERT", 6) == 0 ||
  		strncmp(res->cmdStatus, "DELETE", 6) == 0 ||
***************
*** 1384,1392 ****
  
  		if (*p == 0)
  		{
! 			fprintf(stderr, "PQcmdTuples (%s) -- bad input from server\n",
  					res->cmdStatus);
! 			return NULL;
  		}
  		p++;
  		if (*(res->cmdStatus) != 'I')	/* UPDATE/DELETE */
--- 1382,1392 ----
  
  		if (*p == 0)
  		{
! 			sprintf(res->conn->errorMessage,
! 					"PQcmdTuples (%s) -- bad input from server\n",
  					res->cmdStatus);
! 			DONOTICE(res->conn, res->conn->errorMessage);
! 			return "";
  		}
  		p++;
  		if (*(res->cmdStatus) != 'I')	/* UPDATE/DELETE */
***************
*** 1395,1402 ****
  			p++;				/* INSERT: skip oid */
  		if (*p == 0)
  		{
! 			fprintf(stderr, "PQcmdTuples (INSERT) -- there's no # of tuples\n");
! 			return NULL;
  		}
  		p++;
  		return (p);
--- 1395,1404 ----
  			p++;				/* INSERT: skip oid */
  		if (*p == 0)
  		{
! 			sprintf(res->conn->errorMessage,
! 					"PQcmdTuples (INSERT) -- there's no # of tuples\n");
! 			DONOTICE(res->conn, res->conn->errorMessage);
! 			return "";
  		}
  		p++;
  		return (p);
***************
*** 1417,1444 ****
  char *
  PQgetvalue(PGresult *res, int tup_num, int field_num)
  {
! 	if (!res)
! 	{
! 		fprintf(stderr, "PQgetvalue: pointer to PQresult is null\n");
! 		return NULL;
! 	}
! 	if (tup_num < 0 || tup_num >= res->ntups)
! 	{
! 		fprintf(stderr,
! 				"PQgetvalue: There is no row %d in the query results.  "
! 				"The highest numbered row is %d.\n",
! 				tup_num, res->ntups - 1);
  		return NULL;
- 	}
- 	if (field_num < 0 || field_num >= res->numAttributes)
- 	{
- 		fprintf(stderr,
- 				"PQgetvalue: There is no field %d in the query results.  "
- 				"The highest numbered field is %d.\n",
- 				field_num, res->numAttributes - 1);
- 		return NULL;
- 	}
- 
  	return res->tuples[tup_num][field_num].value;
  }
  
--- 1419,1426 ----
  char *
  PQgetvalue(PGresult *res, int tup_num, int field_num)
  {
! 	if (! check_tuple_field_number("PQgetvalue", res, tup_num, field_num))
  		return NULL;
  	return res->tuples[tup_num][field_num].value;
  }
  
***************
*** 1450,1478 ****
  int
  PQgetlength(PGresult *res, int tup_num, int field_num)
  {
! 	if (!res)
! 	{
! 		fprintf(stderr, "PQgetlength() -- pointer to PQresult is null\n");
! 		return 0;
! 	}
! 
! 	if (tup_num < 0 || tup_num >= res->ntups)
! 	{
! 		fprintf(stderr,
! 				"PQgetlength: There is no row %d in the query results.  "
! 				"The highest numbered row is %d.\n",
! 				tup_num, res->ntups - 1);
! 		return 0;
! 	}
! 	if (field_num < 0 || field_num >= res->numAttributes)
! 	{
! 		fprintf(stderr,
! 				"PQgetlength: There is no field %d in the query results.  "
! 				"The highest numbered field is %d.\n",
! 				field_num, res->numAttributes - 1);
  		return 0;
- 	}
- 
  	if (res->tuples[tup_num][field_num].len != NULL_LEN)
  		return res->tuples[tup_num][field_num].len;
  	else
--- 1432,1439 ----
  int
  PQgetlength(PGresult *res, int tup_num, int field_num)
  {
! 	if (! check_tuple_field_number("PQgetlength", res, tup_num, field_num))
  		return 0;
  	if (res->tuples[tup_num][field_num].len != NULL_LEN)
  		return res->tuples[tup_num][field_num].len;
  	else
***************
*** 1485,1512 ****
  int
  PQgetisnull(PGresult *res, int tup_num, int field_num)
  {
! 	if (!res)
! 	{
! 		fprintf(stderr, "PQgetisnull() -- pointer to PQresult is null\n");
  		return 1;				/* pretend it is null */
- 	}
- 	if (tup_num < 0 || tup_num >= res->ntups)
- 	{
- 		fprintf(stderr,
- 				"PQgetisnull: There is no row %d in the query results.  "
- 				"The highest numbered row is %d.\n",
- 				tup_num, res->ntups - 1);
- 		return 1;				/* pretend it is null */
- 	}
- 	if (field_num < 0 || field_num >= res->numAttributes)
- 	{
- 		fprintf(stderr,
- 				"PQgetisnull: There is no field %d in the query results.  "
- 				"The highest numbered field is %d.\n",
- 				field_num, res->numAttributes - 1);
- 		return 1;				/* pretend it is null */
- 	}
- 
  	if (res->tuples[tup_num][field_num].len == NULL_LEN)
  		return 1;
  	else
--- 1446,1453 ----
  int
  PQgetisnull(PGresult *res, int tup_num, int field_num)
  {
! 	if (! check_tuple_field_number("PQgetisnull", res, tup_num, field_num))
  		return 1;				/* pretend it is null */
  	if (res->tuples[tup_num][field_num].len == NULL_LEN)
  		return 1;
  	else
*** src/interfaces/libpq/fe-misc.c.orig	Fri Jul  3 00:24:14 1998
--- src/interfaces/libpq/fe-misc.c	Sat Aug  8 19:57:18 1998
***************
*** 50,55 ****
--- 50,59 ----
  #include "postgres.h"
  #include "libpq-fe.h"
  
+ #define DONOTICE(conn,message) \
+ 	((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
+ 
+ 
  /* --------------------------------------------------------------------- */
  /* pqGetc:
     get a character from the connection
***************
*** 218,224 ****
  			*result = (int) ntohl(tmp4);
  			break;
  		default:
! 			fprintf(stderr, "** int size %d not supported\n", bytes);
  			return EOF;
  	}
  
--- 222,230 ----
  			*result = (int) ntohl(tmp4);
  			break;
  		default:
! 			sprintf(conn->errorMessage,
! 					"pqGetInt: int size %d not supported\n", bytes);
! 			DONOTICE(conn, conn->errorMessage);
  			return EOF;
  	}
  
***************
*** 252,258 ****
  				return EOF;
  			break;
  		default:
! 			fprintf(stderr, "** int size %d not supported\n", bytes);
  			return EOF;
  	}
  
--- 258,266 ----
  				return EOF;
  			break;
  		default:
! 			sprintf(conn->errorMessage,
! 					"pqPutInt: int size %d not supported\n", bytes);
! 			DONOTICE(conn, conn->errorMessage);
  			return EOF;
  	}
  
***************
*** 265,271 ****
  /* --------------------------------------------------------------------- */
  /* pqReadReady: is select() saying the file is ready to read?
   */
! static int
  pqReadReady(PGconn *conn)
  {
  	fd_set			input_mask;
--- 273,279 ----
  /* --------------------------------------------------------------------- */
  /* pqReadReady: is select() saying the file is ready to read?
   */
! int
  pqReadReady(PGconn *conn)
  {
  	fd_set			input_mask;
*** src/interfaces/libpq/fe-connect.c.orig	Sat Aug  1 15:56:30 1998
--- src/interfaces/libpq/fe-connect.c	Sat Aug  8 19:56:50 1998
***************
*** 29,35 ****
  #include <ctype.h>
  #include <string.h>
  #include <errno.h>
- #include <signal.h>
  #include <ctype.h>				/* for isspace() */
  
  #include "postgres.h"
--- 29,34 ----
***************
*** 55,60 ****
--- 54,60 ----
  static int	conninfo_parse(const char *conninfo, char *errorMessage);
  static char *conninfo_getval(char *keyword);
  static void conninfo_free(void);
+ static void defaultNoticeProcessor(void * arg, const char * message);
  /* XXX Why is this not static? */
  void		PQsetenv(PGconn *conn);
  
***************
*** 181,191 ****
  	 */
  	conn = makeEmptyPGconn();
  	if (conn == NULL)
- 	{
- 		fprintf(stderr,
- 		   "FATAL: PQconnectdb() -- unable to allocate memory for a PGconn");
  		return (PGconn *) NULL;
- 	}
  
  	/* ----------
  	 * Parse the conninfo string and save settings in conn structure
--- 181,187 ----
***************
*** 297,307 ****
  
  	conn = makeEmptyPGconn();
  	if (conn == NULL)
- 	{
- 		fprintf(stderr,
- 		   "FATAL: PQsetdbLogin() -- unable to allocate memory for a PGconn");
  		return (PGconn *) NULL;
- 	}
  
  	if ((pghost == NULL) || pghost[0] == '\0')
  	{
--- 293,299 ----
***************
*** 856,861 ****
--- 848,854 ----
  	/* Zero all pointers */
  	MemSet((char *) conn, 0, sizeof(PGconn));
  
+ 	conn->noticeHook = defaultNoticeProcessor;
  	conn->status = CONNECTION_BAD;
  	conn->asyncStatus = PGASYNC_IDLE;
  	conn->notifyList = DLNewList();
***************
*** 925,959 ****
  	if (conn->sock >= 0)
  	{
  		/*
! 		 * Try to send close message.
! 		 * If connection is already gone, that's cool.  No reason for kernel
! 		 * to kill us when we try to write to it.  So ignore SIGPIPE signals.
  		 */
! #ifndef WIN32
! #if defined(USE_POSIX_SIGNALS)
! 		struct sigaction ignore_action;
! 		struct sigaction oldaction;
! 
! 		ignore_action.sa_handler = SIG_IGN;
! 		sigemptyset(&ignore_action.sa_mask);
! 		ignore_action.sa_flags = 0;
! 		sigaction(SIGPIPE, (struct sigaction *) & ignore_action, &oldaction);
! 
! 		(void) pqPuts("X", conn);
! 		(void) pqFlush(conn);
! 
! 		sigaction(SIGPIPE, &oldaction, NULL);
! #else
! 		void (*oldsignal)(int);
! 
! 		oldsignal = signal(SIGPIPE, SIG_IGN);
! 
! 		(void) pqPuts("X", conn);
! 		(void) pqFlush(conn);
! 
! 		signal(SIGPIPE, oldsignal);
! #endif
! #endif /* Win32 uses no signals at all */
  	}
  
  	/*
--- 918,937 ----
  	if (conn->sock >= 0)
  	{
  		/*
! 		 * Try to send "close connection" message to backend.
! 		 * BUT: backend might have already closed connection.
! 		 * To avoid being killed by SIGPIPE, we need to detect this before
! 		 * writing.  Check for "read ready" condition which indicates EOF.
  		 */
! 		while (pqReadReady(conn)) {
! 			if (pqReadData(conn) < 0)
! 				break;
! 		}
! 		if (conn->sock >= 0) {
! 			/* Should be safe now... */
! 			(void) pqPuts("X", conn);
! 			(void) pqFlush(conn);
! 		}
  	}
  
  	/*
***************
*** 987,995 ****
  void
  PQfinish(PGconn *conn)
  {
! 	if (!conn)
! 		fprintf(stderr, "PQfinish() -- pointer to PGconn is null\n");
! 	else
  	{
  		closePGconn(conn);
  		freePGconn(conn);
--- 965,971 ----
  void
  PQfinish(PGconn *conn)
  {
! 	if (conn)
  	{
  		closePGconn(conn);
  		freePGconn(conn);
***************
*** 1003,1011 ****
  void
  PQreset(PGconn *conn)
  {
! 	if (!conn)
! 		fprintf(stderr, "PQreset() -- pointer to PGconn is null\n");
! 	else
  	{
  		closePGconn(conn);
  		conn->status = connectDB(conn);
--- 979,985 ----
  void
  PQreset(PGconn *conn)
  {
! 	if (conn)
  	{
  		closePGconn(conn);
  		conn->status = connectDB(conn);
***************
*** 1383,1392 ****
  PQdb(PGconn *conn)
  {
  	if (!conn)
- 	{
- 		fprintf(stderr, "PQdb() -- pointer to PGconn is null\n");
  		return (char *) NULL;
- 	}
  	return conn->dbName;
  }
  
--- 1357,1363 ----
***************
*** 1394,1403 ****
  PQuser(PGconn *conn)
  {
  	if (!conn)
- 	{
- 		fprintf(stderr, "PQuser() -- pointer to PGconn is null\n");
  		return (char *) NULL;
- 	}
  	return conn->pguser;
  }
  
--- 1365,1371 ----
***************
*** 1405,1415 ****
  PQhost(PGconn *conn)
  {
  	if (!conn)
- 	{
- 		fprintf(stderr, "PQhost() -- pointer to PGconn is null\n");
  		return (char *) NULL;
- 	}
- 
  	return conn->pghost;
  }
  
--- 1373,1379 ----
***************
*** 1417,1426 ****
  PQoptions(PGconn *conn)
  {
  	if (!conn)
- 	{
- 		fprintf(stderr, "PQoptions() -- pointer to PGconn is null\n");
  		return (char *) NULL;
- 	}
  	return conn->pgoptions;
  }
  
--- 1381,1387 ----
***************
*** 1428,1437 ****
  PQtty(PGconn *conn)
  {
  	if (!conn)
- 	{
- 		fprintf(stderr, "PQtty() -- pointer to PGconn is null\n");
  		return (char *) NULL;
- 	}
  	return conn->pgtty;
  }
  
--- 1389,1395 ----
***************
*** 1439,1448 ****
  PQport(PGconn *conn)
  {
  	if (!conn)
- 	{
- 		fprintf(stderr, "PQport() -- pointer to PGconn is null\n");
  		return (char *) NULL;
- 	}
  	return conn->pgport;
  }
  
--- 1397,1403 ----
***************
*** 1450,1470 ****
  PQstatus(PGconn *conn)
  {
  	if (!conn)
- 	{
- 		fprintf(stderr, "PQstatus() -- pointer to PGconn is null\n");
  		return CONNECTION_BAD;
- 	}
  	return conn->status;
  }
  
  char *
  PQerrorMessage(PGconn *conn)
  {
  	if (!conn)
! 	{
! 		fprintf(stderr, "PQerrorMessage() -- pointer to PGconn is null\n");
! 		return (char *) NULL;
! 	}
  	return conn->errorMessage;
  }
  
--- 1405,1420 ----
  PQstatus(PGconn *conn)
  {
  	if (!conn)
  		return CONNECTION_BAD;
  	return conn->status;
  }
  
  char *
  PQerrorMessage(PGconn *conn)
  {
+ 	static char noConn[] = "PQerrorMessage: conn pointer is NULL\n";
  	if (!conn)
! 		return noConn;
  	return conn->errorMessage;
  }
  
***************
*** 1472,1481 ****
  PQsocket(PGconn *conn)
  {
  	if (!conn)
- 	{
- 		fprintf(stderr, "PQsocket() -- pointer to PGconn is null\n");
  		return -1;
- 	}
  	return conn->sock;
  }
  
--- 1422,1428 ----
***************
*** 1500,1503 ****
--- 1447,1473 ----
  		fflush(conn->Pfdebug);
  		conn->Pfdebug = NULL;
  	}
+ }
+ 
+ void
+ PQsetNoticeProcessor (PGconn *conn, PQnoticeProcessor proc, void *arg)
+ {
+ 	if (conn == NULL)
+ 		return;
+ 	conn->noticeHook = proc;
+ 	conn->noticeArg = arg;
+ }
+ 
+ /*
+  * The default notice/error message processor just prints the
+  * message on stderr.  Applications can override this if they
+  * want the messages to go elsewhere (a window, for example).
+  * Note that simply discarding notices is probably a bad idea.
+  */
+ 
+ static void
+ defaultNoticeProcessor(void * arg, const char * message)
+ {
+ 	/* Note: we expect the supplied string to end with a newline already. */
+ 	fprintf(stderr, "%s", message);
  }
*** src/man/libpq.3.orig	Wed Jul 15 13:34:06 1998
--- src/man/libpq.3	Sat Aug  8 20:10:16 1998
***************
*** 1,7 ****
  .\" This is -*-nroff-*-
  .\" XXX standard disclaimer belongs here....
  .\" $Header: /usr/local/cvsroot/pgsql/src/man/libpq.3,v 1.22 1998/07/15 17:34:06 momjian Exp $
! .TH LIBPQ INTRO 07/08/98 PostgreSQL PostgreSQL
  .SH DESCRIPTION
  Libpq is the programmer's interface to Postgres.  Libpq is a set of
  library routines which allows
--- 1,7 ----
  .\" This is -*-nroff-*-
  .\" XXX standard disclaimer belongs here....
  .\" $Header: /usr/local/cvsroot/pgsql/src/man/libpq.3,v 1.22 1998/07/15 17:34:06 momjian Exp $
! .TH LIBPQ INTRO 08/08/98 PostgreSQL PostgreSQL
  .SH DESCRIPTION
  Libpq is the programmer's interface to Postgres.  Libpq is a set of
  library routines which allows
***************
*** 823,828 ****
--- 823,858 ----
  .nf
  void PQuntrace(PGconn *conn)
  .fi
+ 
+ .PP
+ .SH "LIBPQ Control Functions"
+ .PP
+ .B PQsetNoticeProcessor
+ .IP
+ Control reporting of notice and warning messages generated by libpq.
+ .nf
+ void PQsetNoticeProcessor (PGconn * conn,
+ 		void (*noticeProcessor) (void * arg, const char * message),
+ 		void * arg)
+ .fi
+ By default, libpq prints "notice" messages from the backend on stderr,
+ as well as a few error messages that it generates by itself.
+ This behavior can be overridden by supplying a callback function that
+ does something else with the messages.  The callback function is passed
+ the text of the error message (which includes a trailing newline), plus
+ a void pointer that is the same one passed to PQsetNoticeProcessor.
+ (This pointer can be used to access application-specific state if needed.)
+ The default notice processor is simply
+ .nf
+ static void
+ defaultNoticeProcessor(void * arg, const char * message)
+ {
+ 	fprintf(stderr, "%s", message);
+ }
+ .fi
+ To use a special notice processor, call PQsetNoticeProcessor just after
+ any creation of a new PGconn object.
+ 
  .PP
  .SH "User Authentication Functions"
  .PP

Responses

pgsql-interfaces by date

Next:From: Tom LaneDate: 1998-08-09 00:51:18
Subject: Re: [HACKERS] thread-safe libpq and DBD::Pg
Previous:From: Tom LaneDate: 1998-08-08 18:30:37
Subject: Re: [HACKERS] thread-safe libpq and DBD::Pg

Privacy Policy | About PostgreSQL
Copyright © 1996-2014 The PostgreSQL Global Development Group