-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.360 2007/02/16 03:50:29 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.361 2007/02/16 07:46:54 petere Exp $ -->
<chapter id="functions">
<title>Functions and Operators</title>
</sect3>
</sect2>
+ <sect2>
+ <title>Mapping Tables to XML</title>
+
+ <para>
+ The following functions map the contents of relational tables to
+ XML values. They can be thought of as XML export functionality.
+<synopsis>
+table_to_xml(tbl regclass, nulls boolean, tableforest boolean, targetns text)
+query_to_xml(query text, nulls boolean, tableforest boolean, targetns text)
+cursor_to_xml(cursor refcursor, count int, nulls boolean, tableforest boolean, targetns text)
+</synopsis>
+ The return type of each function is <type>xml</type>.
+ </para>
+
+ <para>
+ <function>table_to_xml</function> maps the content of the named
+ table, passed as parameter <parameter>tbl</parameter>. The
+ <type>regclass</type> accepts strings identifying tables using the
+ usual notation, including optional schema qualifications and
+ double quotes. <function>query_to_xml</function> executes the
+ query whose text is passed as parameter
+ <parameter>query</parameter> and maps the result set.
+ <function>cursor_to_xml</function> fetches the indicated number of
+ rows from the cursor specificed by the parameter
+ <parameter>cursor</parameter>. This variant is recommendable if
+ large tables have to be mapped, because the result value is built
+ up in memory by each function.
+ </para>
+
+ <para>
+ If <parameter>tableforest</parameter> is false, then the resulting
+ XML document looks like this:
+<screen><![CDATA[
+<tablename>
+ <row>
+ <columnname1>data</columnname1>
+ <columnname2>data</columnname2>
+ </row>
+
+ <row>
+ ...
+ </row>
+
+ ...
+</tablename>
+]]></screen>
+
+ If <parameter>tableforest</parameter> is true, the result is an
+ XML content fragment that looks like this:
+<screen><![CDATA[
+<tablename>
+ <columnname1>data</columnname1>
+ <columnname2>data</columnname2>
+</tablename>
+
+<tablename>
+ ...
+</tablename>
+
+...
+]]></screen>
+
+ If no table name is avaible, that is, when mapping a query or a
+ cursor, the string <literal>table</literal> is used in the first
+ format, <literal>row</literal> in the second format.
+ </para>
+
+ <para>
+ The choice between these formats is up to the user. The first
+ format is a proper XML document, which will be important in many
+ applications. The second format tends to be more useful in the
+ <function>cursor_to_xml</function> function if the result values are to be
+ reassembled into one document later on. The functions for
+ producing XML content discussed above, in particular
+ <function>xmlelement</function>, can be used to alter the results
+ to taste.
+ </para>
+
+ <para>
+ The data values are mapping in the same way as described for the
+ function <function>xmlelement</function> above.
+ </para>
+
+ <para>
+ The parameter <parameter>nulls</parameter> determines whether null
+ values should be included in the output. If true, null values in
+ columns are represented as
+<screen><![CDATA[
+<columname xsi:nil="true"/>
+]]></screen>
+ where <literal>xsi</literal> is the XML namespace prefix for XML
+ Schema Instance. An appropriate namespace declaration will be
+ added to the result value. If false, columns containing null
+ values are simply omitted from the output.
+ </para>
+
+ <para>
+ The parameter <parameter>targetns</parameter> specifies the
+ desired XML namespace of the result. If no particular namespace
+ is wanted, an empty string should be passed.
+ </para>
+
+ <para>
+ The following functions return XML Schema documents describing the
+ mappings made by the data mappings produced by the corresponding
+ functions above.
+<synopsis>
+table_to_xmlschema(tbl regclass, nulls boolean, tableforest boolean, targetns text)
+query_to_xmlschema(query text, nulls boolean, tableforest boolean, targetns text)
+cursor_to_xmlschema(cursor refcursor, nulls boolean, tableforest boolean, targetns text)
+</synopsis>
+ It is essential that the same parameters are passed in order to
+ obtain matching XML data mappings and XML Schema documents.
+ </para>
+
+ <para>
+ The following functions produce XML data mappings and the
+ corresponding XML Schema in one document (or forest), linked
+ together. They can be useful where self-contained and
+ self-describing results are wanted.
+<synopsis>
+table_to_xml_and_xmlschema(tbl regclass, nulls boolean, tableforest boolean, targetns text)
+query_to_xml_and_xmlschema(query text, nulls boolean, tableforest boolean, targetns text)
+</synopsis>
+ </para>
+
+ <para>
+ As an example for using the output produced by these functions,
+ <xref linkend="xslt-xml-html"> shows an XSLT stylesheet that
+ converts the output of
+ <function>table_to_xml_and_xmlschema</function> to an HTML
+ document containing a tabular rendition of the table data. In a
+ similar manner, the result data of these functions can be
+ converted into other XML-based formats.
+ </para>
+
+ <figure id="xslt-xml-html">
+ <title>XSLT stylesheet for converting SQL/XML output to HTML</title>
+<programlisting><![CDATA[
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns="http://www.w3.org/1999/xhtml"
+>
+
+ <xsl:output method="xml"
+ doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
+ doctype-public="-//W3C/DTD XHTML 1.0 Strict//EN"
+ indent="yes"/>
+
+ <xsl:template match="/*">
+ <xsl:variable name="schema" select="//xsd:schema"/>
+ <xsl:variable name="tabletypename"
+ select="$schema/xsd:element[@name=name(current())]/@type"/>
+ <xsl:variable name="rowtypename"
+ select="$schema/xsd:complexType[@name=$tabletypename]/xsd:sequence/xsd:element[@name='row']/@type"/>
+
+ <html>
+ <head>
+ <title><xsl:value-of select="name(current())"/></title>
+ </head>
+ <body>
+ <table>
+ <tr>
+ <xsl:for-each select="$schema/xsd:complexType[@name=$rowtypename]/xsd:sequence/xsd:element/@name">
+ <th><xsl:value-of select="."/></th>
+ </xsl:for-each>
+ </tr>
+
+ <xsl:for-each select="row">
+ <tr>
+ <xsl:for-each select="*">
+ <td><xsl:value-of select="."/></td>
+ </xsl:for-each>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </body>
+ </html>
+ </xsl:template>
+
+</xsl:stylesheet>
+]]></programlisting>
+ </figure>
+ </sect2>
+
<sect2>
<title>Processing XML</title>
</para>
<variablelist>
- <varlistentry>
- <term>Import/Export</term>
- <listitem>
-
- <para>
- There is no facility for mapping <acronym>XML</> to relational
- tables. An external tool must be used for this. One simple way
- to export <acronym>XML</> is to use <application>psql</> in
- <acronym>HTML</> mode (<literal>\pset format html</>), and
- convert the <acronym>XHTML</> output to XML using an external
- tool.
- </para>
- </listitem>
- </varlistentry>
-
<varlistentry>
<term>Indexing</term>
<listitem>
* 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.28 2007/02/13 15:56:12 mha Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.29 2007/02/16 07:46:54 petere Exp $
*
*-------------------------------------------------------------------------
*/
#include <libxml/xmlwriter.h>
#endif /* USE_LIBXML */
+#include "catalog/namespace.h"
#include "catalog/pg_type.h"
+#include "commands/dbcommands.h"
#include "executor/executor.h"
+#include "executor/spi.h"
#include "fmgr.h"
+#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
#include "nodes/execnodes.h"
#include "parser/parse_expr.h"
#include "utils/array.h"
#endif /* USE_LIBXML */
+static StringInfo query_to_xml_internal(const char *query, char *tablename, const char *xmlschema, bool nulls, bool tableforest, const char *targetns);
+static const char * map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls, bool tableforest, const char *targetns);
+static const char * map_sql_type_to_xml_name(Oid typeoid, int typmod);
+static const char * map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc);
+static const char * map_sql_type_to_xmlschema_type(Oid typeoid, int typmod);
+static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns);
+
+
XmlBinaryType xmlbinary;
XmlOptionType xmloption;
errmsg("no XML support in this installation")))
+#define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
+#define _textout(x) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(x)))
+
+
+/* from SQL/XML:2003 section 4.7 */
+#define NAMESPACE_XSD "http://www.w3.org/2001/XMLSchema"
+#define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance"
+#define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml"
+
+
Datum
xml_in(PG_FUNCTION_ARGS)
{
{
appendBinaryStringInfo(str, VARDATA(t), VARSIZE(t) - VARHDRSZ);
}
+#endif
static xmltype *
}
-#ifdef NOT_USED
static xmltype *
cstring_to_xmltype(const char *string)
{
return result;
}
-#endif
+#ifdef USE_LIBXML
static xmltype *
xmlBuffer_to_xmltype(xmlBufferPtr buf)
{
return buf.data;
}
+
+
+static char *
+_SPI_strdup(const char *s)
+{
+ char *ret = SPI_palloc(strlen(s) + 1);
+ strcpy(ret, s);
+ return ret;
+}
+
+
+/*
+ * Map SQL table to XML and/or XML Schema document; see SQL/XML:2003
+ * section 9.3.
+ */
+
+Datum
+table_to_xml(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ bool nulls = PG_GETARG_BOOL(1);
+ bool tableforest = PG_GETARG_BOOL(2);
+ const char *targetns = _textout(PG_GETARG_TEXT_P(3));
+
+ StringInfoData query;
+
+ initStringInfo(&query);
+ appendStringInfo(&query, "SELECT * FROM %s", DatumGetCString(DirectFunctionCall1(regclassout, ObjectIdGetDatum(relid))));
+
+ PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query.data, get_rel_name(relid), NULL, nulls, tableforest, targetns)));
+}
+
+
+Datum
+query_to_xml(PG_FUNCTION_ARGS)
+{
+ char *query = _textout(PG_GETARG_TEXT_P(0));
+ bool nulls = PG_GETARG_BOOL(1);
+ bool tableforest = PG_GETARG_BOOL(2);
+ const char *targetns = _textout(PG_GETARG_TEXT_P(3));
+
+ PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL, NULL, nulls, tableforest, targetns)));
+}
+
+
+Datum
+cursor_to_xml(PG_FUNCTION_ARGS)
+{
+ char *name = _textout(PG_GETARG_TEXT_P(0));
+ int32 count = PG_GETARG_INT32(1);
+ bool nulls = PG_GETARG_BOOL(2);
+ bool tableforest = PG_GETARG_BOOL(3);
+ const char *targetns = _textout(PG_GETARG_TEXT_P(4));
+
+ StringInfoData result;
+ Portal portal;
+ int i;
+
+ initStringInfo(&result);
+
+ SPI_connect();
+ portal = SPI_cursor_find(name);
+ if (portal == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_CURSOR),
+ errmsg("cursor \"%s\" does not exist", name)));
+
+ SPI_cursor_fetch(portal, true, count);
+ for (i = 0; i < SPI_processed; i++)
+ SPI_sql_row_to_xmlelement(i, &result, NULL, nulls, tableforest, targetns);
+
+ SPI_finish();
+
+ PG_RETURN_XML_P(stringinfo_to_xmltype(&result));
+}
+
+
+static StringInfo
+query_to_xml_internal(const char *query, char *tablename, const char *xmlschema, bool nulls, bool tableforest, const char *targetns)
+{
+ StringInfo result;
+ char *xmltn;
+ int i;
+
+ if (tablename)
+ xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
+ else
+ xmltn = "table";
+
+ result = makeStringInfo();
+
+ SPI_connect();
+ if (SPI_execute(query, true, 0) != SPI_OK_SELECT)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("invalid query")));
+
+ if (!tableforest)
+ {
+ appendStringInfo(result, "<%s xmlns:xsi=\"" NAMESPACE_XSI "\"", xmltn);
+ if (strlen(targetns) > 0)
+ appendStringInfo(result, " xmlns=\"%s\"", targetns);
+ if (strlen(targetns) > 0)
+ appendStringInfo(result, " xmlns:xsd=\"%s\"", targetns);
+ if (xmlschema)
+ {
+ if (strlen(targetns) > 0)
+ appendStringInfo(result, " xsi:schemaLocation=\"%s #\"", targetns);
+ else
+ appendStringInfo(result, " xsi:noNamespaceSchemaLocation=\"#\"");
+ }
+ appendStringInfo(result, ">\n\n");
+ }
+
+ if (xmlschema)
+ appendStringInfo(result, "%s\n\n", xmlschema);
+
+ for(i = 0; i < SPI_processed; i++)
+ SPI_sql_row_to_xmlelement(i, result, tablename, nulls, tableforest, targetns);
+
+ if (!tableforest)
+ appendStringInfo(result, "</%s>\n", xmltn);
+
+ SPI_finish();
+
+ return result;
+}
+
+
+Datum
+table_to_xmlschema(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ bool nulls = PG_GETARG_BOOL(1);
+ bool tableforest = PG_GETARG_BOOL(2);
+ const char *targetns = _textout(PG_GETARG_TEXT_P(3));
+
+ const char *result;
+ Relation rel;
+
+ rel = heap_open(relid, AccessShareLock);
+ result = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls, tableforest, targetns);
+ heap_close(rel, NoLock);
+
+ PG_RETURN_XML_P(cstring_to_xmltype(result));
+}
+
+
+Datum
+query_to_xmlschema(PG_FUNCTION_ARGS)
+{
+ char *query = _textout(PG_GETARG_TEXT_P(0));
+ bool nulls = PG_GETARG_BOOL(1);
+ bool tableforest = PG_GETARG_BOOL(2);
+ const char *targetns = _textout(PG_GETARG_TEXT_P(3));
+
+ const char *result;
+ void *plan;
+ Portal portal;
+
+ SPI_connect();
+ plan = SPI_prepare(query, 0, NULL);
+ portal = SPI_cursor_open(NULL, plan, NULL, NULL, true);
+ result = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc, InvalidOid, nulls, tableforest, targetns));
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ PG_RETURN_XML_P(cstring_to_xmltype(result));
+}
+
+
+Datum
+cursor_to_xmlschema(PG_FUNCTION_ARGS)
+{
+ char *name = _textout(PG_GETARG_TEXT_P(0));
+ bool nulls = PG_GETARG_BOOL(1);
+ bool tableforest = PG_GETARG_BOOL(2);
+ const char *targetns = _textout(PG_GETARG_TEXT_P(3));
+
+ const char *xmlschema;
+ Portal portal;
+
+ SPI_connect();
+ portal = SPI_cursor_find(name);
+ if (portal == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_CURSOR),
+ errmsg("cursor \"%s\" does not exist", name)));
+
+ xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc, InvalidOid, nulls, tableforest, targetns));
+ SPI_finish();
+
+ PG_RETURN_XML_P(cstring_to_xmltype(xmlschema));
+}
+
+
+Datum
+table_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ bool nulls = PG_GETARG_BOOL(1);
+ bool tableforest = PG_GETARG_BOOL(2);
+ const char *targetns = _textout(PG_GETARG_TEXT_P(3));
+
+ StringInfoData query;
+ Relation rel;
+ const char *xmlschema;
+
+ rel = heap_open(relid, AccessShareLock);
+ xmlschema = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls, tableforest, targetns);
+ heap_close(rel, NoLock);
+
+ initStringInfo(&query);
+ appendStringInfo(&query, "SELECT * FROM %s", DatumGetCString(DirectFunctionCall1(regclassout, ObjectIdGetDatum(relid))));
+
+ PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query.data, get_rel_name(relid), xmlschema, nulls, tableforest, targetns)));
+}
+
+
+Datum
+query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
+{
+ char *query = _textout(PG_GETARG_TEXT_P(0));
+ bool nulls = PG_GETARG_BOOL(1);
+ bool tableforest = PG_GETARG_BOOL(2);
+ const char *targetns = _textout(PG_GETARG_TEXT_P(3));
+
+ const char *xmlschema;
+ void *plan;
+ Portal portal;
+
+ SPI_connect();
+ plan = SPI_prepare(query, 0, NULL);
+ portal = SPI_cursor_open(NULL, plan, NULL, NULL, true);
+ xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc, InvalidOid, nulls, tableforest, targetns));
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL, xmlschema, nulls, tableforest, targetns)));
+}
+
+
+/*
+ * Map a multi-part SQL name to an XML name; see SQL/XML:2003 section
+ * 9.2.
+ */
+static char *
+map_multipart_sql_identifier_to_xml_name(char *a, char *b, char *c, char *d)
+{
+ StringInfoData result;
+
+ initStringInfo(&result);
+
+ if (a)
+ appendStringInfo(&result, "%s", map_sql_identifier_to_xml_name(a, true, true));
+ if (b)
+ appendStringInfo(&result, ".%s", map_sql_identifier_to_xml_name(b, true, true));
+ if (c)
+ appendStringInfo(&result, ".%s", map_sql_identifier_to_xml_name(c, true, true));
+ if (d)
+ appendStringInfo(&result, ".%s", map_sql_identifier_to_xml_name(d, true, true));
+
+ return result.data;
+}
+
+
+/*
+ * Map an SQL table to an XML Schema document; see SQL/XML:2003
+ * section 9.3.
+ *
+ * Map an SQL table to XML Schema data types; see SQL/XML:2003 section
+ * 9.6.
+ */
+static const char *
+map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls, bool tableforest, const char *targetns)
+{
+ int i;
+ char *xmltn;
+ char *tabletypename;
+ char *rowtypename;
+ StringInfoData result;
+
+ initStringInfo(&result);
+
+ if (relid)
+ {
+ HeapTuple tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0);
+ Form_pg_class reltuple = (Form_pg_class) GETSTRUCT(tuple);
+
+ xmltn = map_sql_identifier_to_xml_name(NameStr(reltuple->relname), true, false);
+
+ tabletypename = map_multipart_sql_identifier_to_xml_name("TableType",
+ get_database_name(MyDatabaseId),
+ get_namespace_name(reltuple->relnamespace),
+ NameStr(reltuple->relname));
+
+ rowtypename = map_multipart_sql_identifier_to_xml_name("RowType",
+ get_database_name(MyDatabaseId),
+ get_namespace_name(reltuple->relnamespace),
+ NameStr(reltuple->relname));
+
+ ReleaseSysCache(tuple);
+ }
+ else
+ {
+ if (tableforest)
+ xmltn = "row";
+ else
+ xmltn = "table";
+
+ tabletypename = "TableType";
+ rowtypename = "RowType";
+ }
+
+ appendStringInfoString(&result,
+ "<xsd:schema\n"
+ " xmlns:xsd=\"" NAMESPACE_XSD "\"");
+ if (strlen(targetns) > 0)
+ appendStringInfo(&result,
+ "\n"
+ " targetNamespace=\"%s\"\n"
+ " elementFormDefault=\"qualified\"",
+ targetns);
+ appendStringInfoString(&result,
+ ">\n\n");
+
+ appendStringInfoString(&result,
+ map_sql_typecoll_to_xmlschema_types(tupdesc));
+
+ appendStringInfo(&result,
+ "<xsd:complexType name=\"%s\">\n"
+ " <xsd:sequence>\n",
+ rowtypename);
+
+ for (i = 0; i < tupdesc->natts; i++)
+ appendStringInfo(&result,
+ " <xsd:element name=\"%s\" type=\"%s\"%s></xsd:element>\n",
+ map_sql_identifier_to_xml_name(NameStr(tupdesc->attrs[i]->attname), true, false),
+ map_sql_type_to_xml_name(tupdesc->attrs[i]->atttypid, -1),
+ nulls ? " nillable=\"true\"" : " minOccurs=\"0\"");
+
+ appendStringInfoString(&result,
+ " </xsd:sequence>\n"
+ "</xsd:complexType>\n\n");
+
+ if (!tableforest)
+ {
+ appendStringInfo(&result,
+ "<xsd:complexType name=\"%s\">\n"
+ " <xsd:sequence>\n"
+ " <xsd:element name=\"row\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n"
+ " </xsd:sequence>\n"
+ "</xsd:complexType>\n\n",
+ tabletypename, rowtypename);
+
+ appendStringInfo(&result,
+ "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
+ xmltn, tabletypename);
+ }
+ else
+ appendStringInfo(&result,
+ "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
+ xmltn, rowtypename);
+
+ appendStringInfoString(&result,
+ "</xsd:schema>");
+
+ return result.data;
+}
+
+
+/*
+ * Map an SQL data type to an XML name; see SQL/XML:2003 section 9.9.
+ */
+static const char *
+map_sql_type_to_xml_name(Oid typeoid, int typmod)
+{
+ StringInfoData result;
+
+ initStringInfo(&result);
+
+ switch(typeoid)
+ {
+ case BPCHAROID:
+ if (typmod == -1)
+ appendStringInfo(&result, "CHAR");
+ else
+ appendStringInfo(&result, "CHAR_%d", typmod - VARHDRSZ);
+ break;
+ case VARCHAROID:
+ if (typmod == -1)
+ appendStringInfo(&result, "VARCHAR");
+ else
+ appendStringInfo(&result, "VARCHAR_%d", typmod - VARHDRSZ);
+ break;
+ case NUMERICOID:
+ if (typmod == -1)
+ appendStringInfo(&result, "NUMERIC");
+ else
+ appendStringInfo(&result, "NUMERIC_%d_%d",
+ ((typmod - VARHDRSZ) >> 16) & 0xffff,
+ (typmod - VARHDRSZ) & 0xffff);
+ break;
+ case INT4OID:
+ appendStringInfo(&result, "INTEGER");
+ break;
+ case INT2OID:
+ appendStringInfo(&result, "SMALLINT");
+ break;
+ case INT8OID:
+ appendStringInfo(&result, "BIGINT");
+ break;
+ case FLOAT4OID:
+ appendStringInfo(&result, "REAL");
+ break;
+ case FLOAT8OID:
+ appendStringInfo(&result, "DOUBLE");
+ break;
+ case BOOLOID:
+ appendStringInfo(&result, "BOOLEAN");
+ break;
+ case TIMEOID:
+ if (typmod == -1)
+ appendStringInfo(&result, "TIME");
+ else
+ appendStringInfo(&result, "TIME_%d", typmod);
+ break;
+ case TIMETZOID:
+ if (typmod == -1)
+ appendStringInfo(&result, "TIME_WTZ");
+ else
+ appendStringInfo(&result, "TIME_WTZ_%d", typmod);
+ break;
+ case TIMESTAMPOID:
+ if (typmod == -1)
+ appendStringInfo(&result, "TIMESTAMP");
+ else
+ appendStringInfo(&result, "TIMESTAMP_%d", typmod);
+ break;
+ case TIMESTAMPTZOID:
+ if (typmod == -1)
+ appendStringInfo(&result, "TIMESTAMP_WTZ");
+ else
+ appendStringInfo(&result, "TIMESTAMP_WTZ_%d", typmod);
+ break;
+ case DATEOID:
+ appendStringInfo(&result, "DATE");
+ break;
+ case XMLOID:
+ appendStringInfo(&result, "XML");
+ break;
+ default:
+ {
+ HeapTuple tuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(typeoid), 0, 0, 0);
+ Form_pg_type typtuple = (Form_pg_type) GETSTRUCT(tuple);
+
+ appendStringInfoString(&result,
+ map_multipart_sql_identifier_to_xml_name((typtuple->typtype == 'd') ? "Domain" : "UDT",
+ get_database_name(MyDatabaseId),
+ get_namespace_name(typtuple->typnamespace),
+ NameStr(typtuple->typname)));
+
+ ReleaseSysCache(tuple);
+ }
+ }
+
+ return result.data;
+}
+
+
+/*
+ * Map a collection of SQL data types to XML Schema data types; see
+ * SQL/XML:2002 section 9.10.
+ */
+static const char *
+map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc)
+{
+ Oid *uniquetypes;
+ int i, j;
+ int len;
+ StringInfoData result;
+
+ initStringInfo(&result);
+
+ uniquetypes = palloc(2 * sizeof(*uniquetypes) * tupdesc->natts);
+ len = 0;
+
+ for (i = 1; i <= tupdesc->natts; i++)
+ {
+ bool already_done = false;
+ Oid type = SPI_gettypeid(tupdesc, i);
+ for (j = 0; j < len; j++)
+ if (type == uniquetypes[j])
+ {
+ already_done = true;
+ break;
+ }
+ if (already_done)
+ continue;
+
+ uniquetypes[len++] = type;
+ }
+
+ /* add base types of domains */
+ for (i = 0; i < len; i++)
+ {
+ bool already_done = false;
+ Oid type = getBaseType(uniquetypes[i]);
+ for (j = 0; j < len; j++)
+ if (type == uniquetypes[j])
+ {
+ already_done = true;
+ break;
+ }
+ if (already_done)
+ continue;
+
+ uniquetypes[len++] = type;
+ }
+
+ for (i = 0; i < len; i++)
+ appendStringInfo(&result, "%s\n", map_sql_type_to_xmlschema_type(uniquetypes[i], -1));
+
+ return result.data;
+}
+
+
+/*
+ * Map an SQL data type to a named XML Schema data type; see SQL/XML
+ * sections 9.11 and 9.15.
+ *
+ * (The distinction between 9.11 and 9.15 is basically that 9.15 adds
+ * a name attribute, which thsi function does. The name-less version
+ * 9.11 doesn't appear to be required anywhere.)
+ */
+static const char *
+map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
+{
+ StringInfoData result;
+ const char *typename = map_sql_type_to_xml_name(typeoid, typmod);
+
+ initStringInfo(&result);
+
+ if (typeoid == XMLOID)
+ {
+ appendStringInfo(&result,
+ "<xsd:complexType mixed=\"true\">\n"
+ " <xsd:sequence>\n"
+ " <xsd:any name=\"element\" minOccurs=\"0\" maxOccurs=\"unbounded\" processContents=\"skip\"/>\n"
+ " </xsd:sequence>\n"
+ "</xsd:complexType>\n");
+ }
+ else
+ {
+ appendStringInfo(&result,
+ "<xsd:simpleType name=\"%s\">\n", typename);
+
+ switch(typeoid)
+ {
+ case BPCHAROID:
+ case VARCHAROID:
+ case TEXTOID:
+ if (typmod != -1)
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:string\">\n"
+ " <xsd:maxLength value=\"%d\"/>\n"
+ " </xsd:restriction>\n",
+ typmod - VARHDRSZ);
+ break;
+
+ case BYTEAOID:
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:%s\">\n"
+ " </xsd:restriction>\n",
+ xmlbinary == XMLBINARY_BASE64 ? "base64Binary" : "hexBinary");
+
+ case NUMERICOID:
+ if (typmod != -1)
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:decimal\">\n"
+ " <xsd:totalDigits value=\"%d\"/>\n"
+ " <xsd:fractionDigits value=\"%d\"/>\n"
+ " </xsd:restriction>\n",
+ ((typmod - VARHDRSZ) >> 16) & 0xffff,
+ (typmod - VARHDRSZ) & 0xffff);
+ break;
+
+ case INT2OID:
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:short\">\n"
+ " <xsd:maxInclusive value=\"%d\"/>\n"
+ " <xsd:minInclusive value=\"%d\"/>\n"
+ " </xsd:restriction>\n",
+ SHRT_MAX, SHRT_MIN);
+ break;
+
+ case INT4OID:
+ appendStringInfo(&result,
+ " <xsd:restriction base='xsd:int'>\n"
+ " <xsd:maxInclusive value=\"%d\"/>\n"
+ " <xsd:minInclusive value=\"%d\"/>\n"
+ " </xsd:restriction>\n",
+ INT_MAX, INT_MIN);
+ break;
+
+ case INT8OID:
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:long\">\n"
+ " <xsd:maxInclusive value=\"" INT64_FORMAT "\"/>\n"
+ " <xsd:minInclusive value=\"" INT64_FORMAT "\"/>\n"
+ " </xsd:restriction>\n",
+ INT64_MAX, INT64_MIN);
+ break;
+
+ case FLOAT4OID:
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:float\"></xsd:restriction>\n");
+ break;
+
+ case FLOAT8OID:
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:double\"></xsd:restriction>\n");
+ break;
+
+ case BOOLOID:
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:boolean\"></xsd:restriction>\n");
+ break;
+
+ case TIMEOID:
+ case TIMETZOID:
+ {
+ const char *tz = (typeoid == TIMETZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
+
+ if (typmod == -1)
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:time\">\n"
+ " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
+ " </xsd:restriction>\n", tz);
+ else if (typmod == 0)
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:time\">\n"
+ " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
+ " </xsd:restriction>\n", tz);
+ else
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:time\">\n"
+ " <xsd:pattern value=\"\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
+ " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
+ break;
+ }
+
+ case TIMESTAMPOID:
+ case TIMESTAMPTZOID:
+ {
+ const char *tz = (typeoid == TIMESTAMPTZOID ? "(+|-)\\p{Nd}{2}:\\p{Nd}{2}" : "");
+
+ if (typmod == -1)
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:time\">\n"
+ " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}(.\\p{Nd}+)?%s\"/>\n"
+ " </xsd:restriction>\n", tz);
+ else if (typmod == 0)
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:time\">\n"
+ " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}%s\"/>\n"
+ " </xsd:restriction>\n", tz);
+ else
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:time\">\n"
+ " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}T\\p{Nd}{2}:\\p{Nd}{2}:\\p{Nd}{2}.\\p{Nd}{%d}%s\"/>\n"
+ " </xsd:restriction>\n", typmod - VARHDRSZ, tz);
+ break;
+ }
+
+ case DATEOID:
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"xsd:date\">\n"
+ " <xsd:pattern value=\"\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}\"/>\n"
+ " </xsd:restriction>\n");
+ break;
+
+ default:
+ if (get_typtype(typeoid) == 'd')
+ {
+ Oid base_typeoid;
+ int32 base_typmod = -1;
+
+ base_typeoid = getBaseTypeAndTypmod(typeoid, &base_typmod);
+
+ appendStringInfo(&result,
+ " <xsd:restriction base=\"%s\">\n",
+ map_sql_type_to_xml_name(base_typeoid, base_typmod));
+ }
+ }
+ appendStringInfo(&result,
+ "</xsd:simpleType>\n");
+ }
+
+ return result.data;
+}
+
+
+/*
+ * Map an SQL row to an XML element, taking the row from the active
+ * SPI cursor. See also SQL/XML:2003 section 9.12.
+ */
+static void
+SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns)
+{
+ int i;
+ char *xmltn;
+
+ if (tablename)
+ xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
+ else
+ {
+ if (tableforest)
+ xmltn = "row";
+ else
+ xmltn = "table";
+ }
+
+ if (tableforest)
+ {
+ appendStringInfo(result, "<%s xmlns:xsi=\"" NAMESPACE_XSI "\"", xmltn);
+ if (strlen(targetns) > 0)
+ appendStringInfo(result, " xmlns=\"%s\"", targetns);
+ appendStringInfo(result, ">\n");
+ }
+ else
+ appendStringInfoString(result, "<row>\n");
+
+ for(i = 1; i <= SPI_tuptable->tupdesc->natts; i++)
+ {
+ char *colname;
+ Datum colval;
+ bool isnull;
+
+ colname = map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable->tupdesc, i), true, false);
+ colval = SPI_getbinval(SPI_tuptable->vals[rownum], SPI_tuptable->tupdesc, i, &isnull);
+
+ if (isnull)
+ {
+ if (nulls)
+ appendStringInfo(result, " <%s xsi:nil='true'/>\n", colname);
+
+ }
+ else
+ appendStringInfo(result, " <%s>%s</%s>\n",
+ colname, map_sql_value_to_xml_value(colval, SPI_gettypeid(SPI_tuptable->tupdesc, i)),
+ colname);
+ }
+
+ if (tableforest)
+ appendStringInfo(result, "</%s>\n\n", xmltn);
+ else
+ appendStringInfoString(result, "</row>\n\n");
+}