Functions for mapping table data and table schemas to XML (a.k.a. XML export)
This commit is contained in:
parent
bb0a8a3ad4
commit
355e05ab41
@ -1,4 +1,4 @@
|
|||||||
<!-- $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">
|
<chapter id="functions">
|
||||||
<title>Functions and Operators</title>
|
<title>Functions and Operators</title>
|
||||||
@ -11156,6 +11156,193 @@ SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'),
|
|||||||
</sect3>
|
</sect3>
|
||||||
</sect2>
|
</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>
|
<sect2>
|
||||||
<title>Processing XML</title>
|
<title>Processing XML</title>
|
||||||
|
|
||||||
@ -11171,21 +11358,6 @@ SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'),
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<variablelist>
|
<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>
|
<varlistentry>
|
||||||
<term>Indexing</term>
|
<term>Indexing</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -49,11 +49,16 @@
|
|||||||
#include <libxml/xmlwriter.h>
|
#include <libxml/xmlwriter.h>
|
||||||
#endif /* USE_LIBXML */
|
#endif /* USE_LIBXML */
|
||||||
|
|
||||||
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
|
#include "commands/dbcommands.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
|
#include "executor/spi.h"
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
|
#include "lib/stringinfo.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "mb/pg_wchar.h"
|
#include "mb/pg_wchar.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
#include "nodes/execnodes.h"
|
#include "nodes/execnodes.h"
|
||||||
#include "parser/parse_expr.h"
|
#include "parser/parse_expr.h"
|
||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
@ -84,6 +89,14 @@ static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg, bool preserv
|
|||||||
|
|
||||||
#endif /* USE_LIBXML */
|
#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;
|
XmlBinaryType xmlbinary;
|
||||||
XmlOptionType xmloption;
|
XmlOptionType xmloption;
|
||||||
|
|
||||||
@ -94,6 +107,16 @@ XmlOptionType xmloption;
|
|||||||
errmsg("no XML support in this installation")))
|
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
|
Datum
|
||||||
xml_in(PG_FUNCTION_ARGS)
|
xml_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@ -259,6 +282,7 @@ appendStringInfoText(StringInfo str, const text *t)
|
|||||||
{
|
{
|
||||||
appendBinaryStringInfo(str, VARDATA(t), VARSIZE(t) - VARHDRSZ);
|
appendBinaryStringInfo(str, VARDATA(t), VARSIZE(t) - VARHDRSZ);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static xmltype *
|
static xmltype *
|
||||||
@ -276,7 +300,6 @@ stringinfo_to_xmltype(StringInfo buf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef NOT_USED
|
|
||||||
static xmltype *
|
static xmltype *
|
||||||
cstring_to_xmltype(const char *string)
|
cstring_to_xmltype(const char *string)
|
||||||
{
|
{
|
||||||
@ -290,9 +313,9 @@ cstring_to_xmltype(const char *string)
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_LIBXML
|
||||||
static xmltype *
|
static xmltype *
|
||||||
xmlBuffer_to_xmltype(xmlBufferPtr buf)
|
xmlBuffer_to_xmltype(xmlBufferPtr buf)
|
||||||
{
|
{
|
||||||
@ -1551,3 +1574,762 @@ map_sql_value_to_xml_value(Datum value, Oid type)
|
|||||||
|
|
||||||
return buf.data;
|
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");
|
||||||
|
}
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.384 2007/02/14 01:58:58 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.385 2007/02/16 07:46:55 petere Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200702131
|
#define CATALOG_VERSION_NO 200702161
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.443 2007/02/07 23:11:30 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.444 2007/02/16 07:46:55 petere Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* The script catalog/genbki.sh reads this file and generates .bki
|
* The script catalog/genbki.sh reads this file and generates .bki
|
||||||
@ -4050,6 +4050,24 @@ DESCR("concatenate XML values");
|
|||||||
DATA(insert OID = 2922 ( text PGNSP PGUID 12 1 0 f f t f s 1 25 "142" _null_ _null_ _null_ xmltotext - _null_ ));
|
DATA(insert OID = 2922 ( text PGNSP PGUID 12 1 0 f f t f s 1 25 "142" _null_ _null_ _null_ xmltotext - _null_ ));
|
||||||
DESCR("serialize an XML value to a character string");
|
DESCR("serialize an XML value to a character string");
|
||||||
|
|
||||||
|
DATA(insert ( table_to_xml PGNSP PGUID 12 100 0 f f t f s 4 142 "2205 16 16 25" _null_ _null_ "{tbl,nulls,tableforest,targetns}" table_to_xml - _null_ ));
|
||||||
|
DESCR("map table contents to XML");
|
||||||
|
DATA(insert ( query_to_xml PGNSP PGUID 12 100 0 f f t f s 4 142 "25 16 16 25" _null_ _null_ "{query,nulls,tableforest,targetns}" query_to_xml - _null_ ));
|
||||||
|
DESCR("map query result to XML");
|
||||||
|
DATA(insert ( cursor_to_xml PGNSP PGUID 12 100 0 f f t f s 5 142 "1790 23 16 16 25" _null_ _null_ "{cursor,count,nulls,tableforest,targetns}" cursor_to_xml - _null_ ));
|
||||||
|
DESCR("map rows from cursor to XML");
|
||||||
|
DATA(insert ( table_to_xmlschema PGNSP PGUID 12 100 0 f f t f s 4 142 "2205 16 16 25" _null_ _null_ "{tbl,nulls,tableforest,targetns}" table_to_xmlschema - _null_ ));
|
||||||
|
DESCR("map table structure to XML Schema");
|
||||||
|
DATA(insert ( query_to_xmlschema PGNSP PGUID 12 100 0 f f t f s 4 142 "25 16 16 25" _null_ _null_ "{query,nulls,tableforest,targetns}" query_to_xmlschema - _null_ ));
|
||||||
|
DESCR("map query result structure to XML Schema");
|
||||||
|
DATA(insert ( cursor_to_xmlschema PGNSP PGUID 12 100 0 f f t f s 4 142 "1790 16 16 25" _null_ _null_ "{cursor,nulls,tableforest,targetns}" cursor_to_xmlschema - _null_ ));
|
||||||
|
DESCR("map cursor structure to XML Schema");
|
||||||
|
DATA(insert ( table_to_xml_and_xmlschema PGNSP PGUID 12 100 0 f f t f s 4 142 "2205 16 16 25" _null_ _null_ "{tbl,nulls,tableforest,targetns}" table_to_xml_and_xmlschema - _null_ ));
|
||||||
|
DESCR("map table contents and structure to XML and XML Schema");
|
||||||
|
DATA(insert ( query_to_xml_and_xmlschema PGNSP PGUID 12 100 0 f f t f s 4 142 "25 16 16 25" _null_ _null_ "{query,nulls,tableforest,targetns}" query_to_xml_and_xmlschema - _null_ ));
|
||||||
|
DESCR("map query result and structure to XML and XML Schema");
|
||||||
|
|
||||||
|
|
||||||
/* uuid */
|
/* uuid */
|
||||||
DATA(insert OID = 2952 ( uuid_in PGNSP PGUID 12 1 0 f f t f i 1 2950 "2275" _null_ _null_ _null_ uuid_in - _null_ ));
|
DATA(insert OID = 2952 ( uuid_in PGNSP PGUID 12 1 0 f f t f i 1 2950 "2275" _null_ _null_ _null_ uuid_in - _null_ ));
|
||||||
DESCR("I/O");
|
DESCR("I/O");
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.15 2007/02/11 22:18:16 petere Exp $
|
* $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.16 2007/02/16 07:46:55 petere Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -37,6 +37,15 @@ extern Datum texttoxml(PG_FUNCTION_ARGS);
|
|||||||
extern Datum xmltotext(PG_FUNCTION_ARGS);
|
extern Datum xmltotext(PG_FUNCTION_ARGS);
|
||||||
extern Datum xmlvalidate(PG_FUNCTION_ARGS);
|
extern Datum xmlvalidate(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
extern Datum table_to_xml(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum query_to_xml(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum cursor_to_xml(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum table_to_xmlschema(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum query_to_xmlschema(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum cursor_to_xmlschema(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum table_to_xml_and_xmlschema(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum query_to_xml_and_xmlschema(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
XML_STANDALONE_YES,
|
XML_STANDALONE_YES,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user