diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index d487cce5cc30b339d42b9bb640e02222c49a034f..34ab2bb59f85042ce4c8177e590a160bce93ed92 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.87 2002/01/18 20:39:04 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.88 2002/03/04 23:59:11 momjian Exp $
 -->
 
  <chapter id="libpq">
@@ -955,6 +955,25 @@ strings overlap.
    byte is also added.  The single quotes that must surround
    PostgreSQL string literals are not part of the result string.
   </para>
+
+  <para>
+   <function>PQunescapeBytea</function>
+   Converts an escaped string representation of binary data into binary
+   data - the reverse of <function>PQescapeBytea</function>.
+   <synopsis>
+    unsigned char *PQunescapeBytea(unsigned char *from, size_t *to_length);
+   </synopsis>
+
+   The <paramater>from</parameter> parameter points to an escaped string
+   such as might be returned by <function>PQgetvalue</function> of a
+   <type>BYTEA</type> column. <function>PQunescapeBytea</function> converts
+   this NUL terminated string representation into binary, filling a buffer.
+   It returns a pointer to the buffer which is NULL on error, and the size
+   of the buffer in <parameter>to_length</parameter>. The pointer may
+   subsequently be used as an argument to the function
+   <function>free(3)</function>.
+  </para>
+   
  </sect2>
 
 
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 0b72865e39da9f490878edcc83d5b98ebef72a49..2d8dc4ea5c217da2fef74235aeeea1637b8e4ea2 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: elog.h,v 1.32 2002/03/04 01:46:04 tgl Exp $
+ * $Id: elog.h,v 1.33 2002/03/04 23:59:14 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,27 +15,28 @@
 #define ELOG_H
 
 /* Error level codes */
-#define DEBUG5	10				/* Debugging messages, in categories
-								 * of decreasing detail. */
-#define DEBUG4	11
-#define DEBUG3	12				
-#define DEBUG2	13				
-#define DEBUG1	14				
-#define LOG		15				/* Server operational history messages;
-								 * sent only to server log by default. */
-#define COMMERROR 16			/* Client communication problems; same as
-								 * LOG for server reporting, but never ever
-								 * try to send to client. */
-#define INFO	17				/* Informative messages that are part of
-								 * normal query operation; sent only to
-								 * client by default. */
-#define NOTICE	18				/* Important messages, for unusual cases that
-								 * should be reported but are not serious 
-								 * enough to abort the query.  Sent to client
-								 * and server log by default. */
-#define ERROR	19				/* user error - return to known state */
-#define FATAL	20				/* fatal error - abort process */
-#define PANIC	21				/* take down the other backends with me */
+#define DEBUG5		10		/* Debugging messages, in categories
+							 * of decreasing detail. */
+#define DEBUG4		11
+#define DEBUG3		12				
+#define DEBUG2		13				
+#define DEBUG1		14				
+#define LOG			15		/* Server operational history messages;
+							 * sent only to server log by default. */
+#define COMMERROR 	16		/* Client communication problems; same as
+							 * LOG for server reporting, but never ever
+							 * try to send to client. */
+#define INFO		17		/* Informative messages that are part of
+							 * normal query operation; sent only to
+							 * client by default. */
+#define INFOALWAYS	18		/* Like INFO, but always prints to client */	
+#define NOTICE		19		/* Important messages, for unusual cases that
+							 * should be reported but are not serious 
+							 * enough to abort the query.  Sent to client
+							 * and server log by default. */
+#define ERROR		20		/* user error - return to known state */
+#define FATAL		21		/* fatal error - abort process */
+#define PANIC		22		/* take down the other backends with me */
 
 /*#define DEBUG	DEBUG1*/		/* Backward compatibility with pre-7.3 */
 
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 123d7abd1296dd25dc51d478add8b7118581b9e3..7eeaefbb55db54eea24122b5dddc78416af1629c 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.113 2001/10/25 05:50:13 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.114 2002/03/04 23:59:14 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -180,6 +180,95 @@ PQescapeBytea(unsigned char *bintext, size_t binlen, size_t *bytealen)
 	return result;
 }
 
+/*
+ *		PQunescapeBytea	- converts the null terminated string representation
+ *		of a bytea, strtext, into binary, filling a buffer. It returns a
+ *		pointer to the buffer which is NULL on error, and the size of the
+ *		buffer in retbuflen. The pointer may subsequently be used as an
+ *		argument to the function free(3). It is the reverse of PQescapeBytea.
+ *
+ *		The following transformations are reversed:
+ *		'\0' == ASCII  0 == \000
+ *		'\'' == ASCII 39 == \'
+ *		'\\' == ASCII 92 == \\
+ *
+ *		States:
+ *		0	normal		0->1->2->3->4
+ *		1	\			   1->5
+ *		2	\0			   1->6
+ *		3	\00
+ *		4	\000
+ *		5	\'
+ *		6	\\
+ */
+unsigned char *
+PQunescapeBytea(unsigned char *strtext, size_t *retbuflen) 
+{
+	size_t buflen;
+	unsigned char *buffer, *sp, *bp;
+	unsigned int state=0;
+
+    if(strtext == NULL)return NULL;
+	buflen = strlen(strtext); /* will shrink, also we discover if strtext */
+	buffer = (unsigned char *) malloc(buflen);   /* isn't NULL terminated */
+    if(buffer == NULL)return NULL;
+	for(bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
+	{
+		switch(state)
+		{
+			case 0:
+				if(*sp == '\\')state=1;
+				*bp = *sp;
+				break;
+			case 1:
+				if(*sp == '\'') /* state=5 */
+				{ /* replace \' with 39 */
+					bp--;
+					*bp = 39;
+					buflen--;
+					state=0;
+				}
+				else if(*sp == '\\') /* state=6 */
+				{ /* replace \\ with 92 */
+					bp--;
+					*bp = 92;
+					buflen--;
+					state=0;
+				}
+				else
+				{
+					if(*sp == '0')state=2;
+					else state=0;
+					*bp = *sp;
+				}
+				break;
+			case 2:
+				if(*sp == '0')state=3;
+				else state=0;
+				*bp = *sp;
+				break;
+			case 3:
+				if(*sp == '0') /* state=4 */
+				{
+					bp -= 3;
+					*bp = 0;
+					buflen -= 3;
+					state=0;
+				}
+				else
+				{
+					*bp = *sp;
+					state=0;
+				}
+				break;
+		}
+	}
+	realloc(buffer,buflen);
+
+	*retbuflen=buflen;
+	return buffer;
+}
+
 /* ----------------
  * Space management for PGresult.
  *
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 02dbe104d06414ea58d53795330c2405c21087e8..b94b5aae0acf53515800c23f668de81606c180b6 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-fe.h,v 1.80 2001/11/08 20:37:52 momjian Exp $
+ * $Id: libpq-fe.h,v 1.81 2002/03/04 23:59:14 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -252,6 +252,9 @@ extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn,
 extern size_t PQescapeString(char *to, const char *from, size_t length);
 extern unsigned char *PQescapeBytea(unsigned char *bintext, size_t binlen,
 			  size_t *bytealen);
+extern unsigned char *PQunescapeBytea(unsigned char *strtext,
+			  size_t *retbuflen);
+
 
 /* Simple synchronous query */
 extern PGresult *PQexec(PGconn *conn, const char *query);