diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 033dd6c75cf3374ec82cf4aa4d0a4ba219a51200..e3c79c0cf5bbf35e11eca8eb1f78137a690fab56 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.205 2007/01/08 23:41:56 petere Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.206 2007/01/12 16:29:24 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1455,10 +1455,6 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
 				newe = coerce_to_specific_type(pstate, newe, XMLOID,
 											   "XMLCONCAT");
 				break;
-			case IS_XMLELEMENT:
-				newe = coerce_to_specific_type(pstate, newe, XMLOID,
-											   "XMLELEMENT");
-				break;
 			case IS_XMLFOREST:
 				newe = coerce_to_specific_type(pstate, newe, XMLOID,
 											   "XMLFOREST");
@@ -1488,7 +1484,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
 		newx->args = lappend(newx->args, newe);
 		i++;
 	}
-		
+
 	return (Node *) newx;
 }
 
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 5616259b29e56a18d76226ea89e1cfd34318b303..3689c9203e8b0ab9f4c0a5c75a963046272c5ad9 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.14 2007/01/10 20:33:54 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.15 2007/01/12 16:29:24 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,12 +35,16 @@
 #include <libxml/xmlwriter.h>
 #endif /* USE_LIBXML */
 
+#include "catalog/pg_type.h"
 #include "executor/executor.h"
 #include "fmgr.h"
 #include "libpq/pqformat.h"
 #include "mb/pg_wchar.h"
 #include "nodes/execnodes.h"
+#include "parser/parse_expr.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/xml.h"
 
@@ -66,6 +70,8 @@ static void 	xml_ereport_by_code(int level, int sqlcode,
 static xmlChar *xml_text2xmlChar(text *in);
 static xmlDocPtr xml_parse(text *data, bool is_document, bool preserve_whitespace);
 
+static char *map_sql_value_to_xml_value(Datum value, Oid type);
+
 #endif /* USE_LIBXML */
 
 #define NO_XML_SUPPORT() \
@@ -284,13 +290,7 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
 
 		value = ExecEvalExpr(e, econtext, &isnull, NULL);
 		if (!isnull)
-		{
-			/* we know the value is XML type */
-			str = DatumGetCString(DirectFunctionCall1(xml_out,
-													  value));
-			xmlTextWriterWriteRaw(writer, (xmlChar *) str);
-			pfree(str);
-		}
+			xmlTextWriterWriteRaw(writer, (xmlChar *) map_sql_value_to_xml_value(value, exprType((Node *) e->expr)));
 	}
 
 	xmlTextWriterEndElement(writer);
@@ -1258,3 +1258,87 @@ map_xml_name_to_sql_identifier(char *name)
 
 	return buf.data;
 }
+
+
+#ifdef USE_LIBXML
+/*
+ * Map SQL value to XML value; see SQL/XML:2003 section 9.16.
+ */
+static char *
+map_sql_value_to_xml_value(Datum value, Oid type)
+{
+	StringInfoData buf;
+
+	initStringInfo(&buf);
+
+	if (is_array_type(type))
+	{
+		int i;
+		ArrayType *array;
+		Oid elmtype;
+		int16 elmlen;
+		bool elmbyval;
+		char elmalign;
+
+		array = DatumGetArrayTypeP(value);
+
+		/* TODO: need some code-fu here to remove this limitation */
+		if (ARR_NDIM(array) != 1)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("only supported for one-dimensional array")));
+
+		elmtype = ARR_ELEMTYPE(array);
+		get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
+
+		for (i = ARR_LBOUND(array)[0];
+			 i < ARR_LBOUND(array)[0] + ARR_DIMS(array)[0];
+			 i++)
+		{
+			Datum subval;
+			bool isnull;
+
+			subval = array_ref(array, 1, &i, -1, elmlen, elmbyval, elmalign, &isnull);
+			appendStringInfoString(&buf, "<element>");
+			appendStringInfoString(&buf, map_sql_value_to_xml_value(subval, elmtype));
+			appendStringInfoString(&buf, "</element>");
+		}
+	}
+	else
+	{
+		Oid typeOut;
+		bool isvarlena;
+		char *p, *str;
+
+		getTypeOutputInfo(type, &typeOut, &isvarlena);
+		str = OidOutputFunctionCall(typeOut, value);
+
+		if (type == XMLOID)
+			return str;
+
+		for (p = str; *p; p += pg_mblen(p))
+		{
+			switch (*p)
+			{
+				case '&':
+					appendStringInfo(&buf, "&amp;");
+					break;
+				case '<':
+					appendStringInfo(&buf, "&lt;");
+					break;
+				case '>':
+					appendStringInfo(&buf, "&gt;");
+					break;
+				case '\r':
+					appendStringInfo(&buf, "&#x0d;");
+					break;
+				default:
+					appendBinaryStringInfo(&buf, p, pg_mblen(p));
+					break;
+			}
+		}
+	}
+
+	return buf.data;
+}
+#endif /* USE_LIBXML */
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index 4179233e99938414e088ce55fa1dc0d0e72a5a0d..275523727b45bcc0daba9a233b420bb313058126 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -83,10 +83,44 @@ SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp;
  <employee><name>linda</name><age>19</age><pay>100</pay></employee>
 (6 rows)
 
-SELECT xmlelement(name wrong, 37);
-ERROR:  argument of XMLELEMENT must be type xml, not type integer
 SELECT xmlelement(name duplicate, xmlattributes(1 as a, 2 as b, 3 as a));
 ERROR:  XML attribute name "a" appears more than once
+SELECT xmlelement(name num, 37);
+  xmlelement   
+---------------
+ <num>37</num>
+(1 row)
+
+SELECT xmlelement(name foo, text 'bar');
+   xmlelement   
+----------------
+ <foo>bar</foo>
+(1 row)
+
+SELECT xmlelement(name foo, xml 'bar');
+   xmlelement   
+----------------
+ <foo>bar</foo>
+(1 row)
+
+SELECT xmlelement(name foo, text 'b<a/>r');
+       xmlelement        
+-------------------------
+ <foo>b&lt;a/&gt;r</foo>
+(1 row)
+
+SELECT xmlelement(name foo, xml 'b<a/>r');
+    xmlelement     
+-------------------
+ <foo>b<a/>r</foo>
+(1 row)
+
+SELECT xmlelement(name foo, array[1, 2, 3]);
+                               xmlelement                                
+-------------------------------------------------------------------------
+ <foo><element>1</element><element>2</element><element>3</element></foo>
+(1 row)
+
 SELECT xmlparse(content 'abc');
  xmlparse 
 ----------
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index 8ef963b8e9db4a4a2f8dab0ed38e63dba59f57cf..9ff3959160e00c60a69fc50ad5fbc13ad30b8b8c 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -44,10 +44,20 @@ SELECT xmlelement(name element, xmlelement(name nested, 'stuff'));
 ERROR:  no XML support in this installation
 SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp;
 ERROR:  no XML support in this installation
-SELECT xmlelement(name wrong, 37);
-ERROR:  no XML support in this installation
 SELECT xmlelement(name duplicate, xmlattributes(1 as a, 2 as b, 3 as a));
 ERROR:  no XML support in this installation
+SELECT xmlelement(name num, 37);
+ERROR:  no XML support in this installation
+SELECT xmlelement(name foo, text 'bar');
+ERROR:  no XML support in this installation
+SELECT xmlelement(name foo, xml 'bar');
+ERROR:  no XML support in this installation
+SELECT xmlelement(name foo, text 'b<a/>r');
+ERROR:  no XML support in this installation
+SELECT xmlelement(name foo, xml 'b<a/>r');
+ERROR:  no XML support in this installation
+SELECT xmlelement(name foo, array[1, 2, 3]);
+ERROR:  no XML support in this installation
 SELECT xmlparse(content 'abc');
 ERROR:  no XML support in this installation
 SELECT xmlparse(content '<abc>x</abc>');
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index d3a1e6104bb87126fe74e52cb930c33918b60efa..a22c825129886a9817b15433d65da163492c53d2 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -37,9 +37,15 @@ SELECT xmlelement(name element, xmlelement(name nested, 'stuff'));
 
 SELECT xmlelement(name employee, xmlforest(name, age, salary as pay)) FROM emp;
 
-SELECT xmlelement(name wrong, 37);
 SELECT xmlelement(name duplicate, xmlattributes(1 as a, 2 as b, 3 as a));
 
+SELECT xmlelement(name num, 37);
+SELECT xmlelement(name foo, text 'bar');
+SELECT xmlelement(name foo, xml 'bar');
+SELECT xmlelement(name foo, text 'b<a/>r');
+SELECT xmlelement(name foo, xml 'b<a/>r');
+SELECT xmlelement(name foo, array[1, 2, 3]);
+
 
 SELECT xmlparse(content 'abc');
 SELECT xmlparse(content '<abc>x</abc>');