Commit 0b75afda authored by Peter Eisentraut's avatar Peter Eisentraut

Mapping schemas and databases to XML and XML Schema.

Refactor and document the remaining mapping code.
parent ae8072a4
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.371 2007/03/25 11:56:01 ishii Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.372 2007/04/01 09:00:24 petere Exp $ -->
<chapter id="functions"> <chapter id="functions">
<title>Functions and Operators</title> <title>Functions and Operators</title>
...@@ -11427,7 +11427,7 @@ cursor_to_xml(cursor refcursor, count int, nulls boolean, tableforest boolean, t ...@@ -11427,7 +11427,7 @@ cursor_to_xml(cursor refcursor, count int, nulls boolean, tableforest boolean, t
query whose text is passed as parameter query whose text is passed as parameter
<parameter>query</parameter> and maps the result set. <parameter>query</parameter> and maps the result set.
<function>cursor_to_xml</function> fetches the indicated number of <function>cursor_to_xml</function> fetches the indicated number of
rows from the cursor specificed by the parameter rows from the cursor specified by the parameter
<parameter>cursor</parameter>. This variant is recommendable if <parameter>cursor</parameter>. This variant is recommendable if
large tables have to be mapped, because the result value is built large tables have to be mapped, because the result value is built
up in memory by each function. up in memory by each function.
...@@ -11492,7 +11492,7 @@ cursor_to_xml(cursor refcursor, count int, nulls boolean, tableforest boolean, t ...@@ -11492,7 +11492,7 @@ cursor_to_xml(cursor refcursor, count int, nulls boolean, tableforest boolean, t
values should be included in the output. If true, null values in values should be included in the output. If true, null values in
columns are represented as columns are represented as
<screen><![CDATA[ <screen><![CDATA[
<columname xsi:nil="true"/> <columnname xsi:nil="true"/>
]]></screen> ]]></screen>
where <literal>xsi</literal> is the XML namespace prefix for XML where <literal>xsi</literal> is the XML namespace prefix for XML
Schema Instance. An appropriate namespace declaration will be Schema Instance. An appropriate namespace declaration will be
...@@ -11530,6 +11530,65 @@ query_to_xml_and_xmlschema(query text, nulls boolean, tableforest boolean, targe ...@@ -11530,6 +11530,65 @@ query_to_xml_and_xmlschema(query text, nulls boolean, tableforest boolean, targe
</synopsis> </synopsis>
</para> </para>
<para>
In addition, the following functions are available to produce
analogous mappings of entire schemas or the entire current
database.
<synopsis>
schema_to_xml(schema name, nulls boolean, tableforest boolean, targetns text)
schema_to_xmlschema(schema name, nulls boolean, tableforest boolean, targetns text)
schema_to_xml_and_xmlschema(schema name, nulls boolean, tableforest boolean, targetns text)
database_to_xml(nulls boolean, tableforest boolean, targetns text)
database_to_xmlschema(nulls boolean, tableforest boolean, targetns text)
database_to_xml_and_xmlschema(nulls boolean, tableforest boolean, targetns text)
</synopsis>
Note that these potentially produce a lot of data, which needs to
be built up in memory. When requesting content mappings of large
schemas or databases, it may be worthwhile to consider mapping the
tables separately instead, possibly even through a cursor.
</para>
<para>
The result of a schema content mapping looks like this:
<screen><![CDATA[
<schemaname>
table1-mapping
table2-mapping
...
</schemaname>]]></screen>
where the format of a table mapping depends on the
<parameter>tableforest</parameter> parameter as explained above.
</para>
<para>
The result of a database content mapping looks like this:
<screen><![CDATA[
<dbname>
<schema1name>
...
</schema1name>
<schema2name>
...
</schema2name>
...
</dbname>]]></screen>
where the schema mapping is as above.
</para>
<para> <para>
As an example for using the output produced by these functions, As an example for using the output produced by these functions,
<xref linkend="xslt-xml-html"> shows an XSLT stylesheet that <xref linkend="xslt-xml-html"> shows an XSLT stylesheet that
......
...@@ -7,7 +7,7 @@ ...@@ -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.37 2007/03/22 20:26:30 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.38 2007/04/01 09:00:25 petere Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -95,12 +95,14 @@ static text *xml_xmlnodetoxmltype(xmlNodePtr cur); ...@@ -95,12 +95,14 @@ static text *xml_xmlnodetoxmltype(xmlNodePtr cur);
#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 StringInfo query_to_xml_internal(const char *query, char *tablename, const char *xmlschema, bool nulls, bool tableforest, const char *targetns, bool top_level);
static const char * map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, 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_schema_to_xmlschema_types(Oid nspid, List *relid_list, bool nulls, bool tableforest, const char *targetns);
static const char * map_sql_catalog_to_xmlschema_types(List *nspid_list, 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_type_to_xml_name(Oid typeoid, int typmod);
static const char * map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc); static const char * map_sql_typecoll_to_xmlschema_types(List *tupdesc_list);
static const char * map_sql_type_to_xmlschema_type(Oid typeoid, int typmod); 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); static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns, bool top_level);
XmlBinaryType xmlbinary; XmlBinaryType xmlbinary;
...@@ -1657,11 +1659,126 @@ _SPI_strdup(const char *s) ...@@ -1657,11 +1659,126 @@ _SPI_strdup(const char *s)
} }
/*
* SQL to XML mapping functions
*
* What follows below is intentionally organized so that you can read
* along in the SQL/XML:2003 standard. The functions are mostly split
* up and ordered they way the clauses lay out in the standards
* document, and the identifiers are also aligned with the standard
* text. (SQL/XML:2006 appears to be ordered differently,
* unfortunately.)
*
* There are many things going on there:
*
* There are two kinds of mappings: Mapping SQL data (table contents)
* to XML documents, and mapping SQL structure (the "schema") to XML
* Schema. And there are functions that do both at the same time.
*
* Then you can map a database, a schema, or a table, each in both
* ways. This breaks down recursively: Mapping a database invokes
* mapping schemas, which invokes mapping tables, which invokes
* mapping rows, which invokes mapping columns, although you can't
* call the last two from the outside. Because of this, there are a
* number of xyz_internal() functions which are to be called both from
* the function manager wrapper and from some upper layer in a
* recursive call.
*
* See the documentation about what the common function arguments
* nulls, tableforest, and targetns mean.
*
* Some style guidelines for XML output: Use double quotes for quoting
* XML attributes. Indent XML elements by two spaces, but remember
* that a lot of code is called recursively at different levels, so
* it's better not to indent rather than create output that indents
* and outdents weirdly. Add newlines to make the output look nice.
*/
/*
* Visibility of objects for XML mappings; see SQL/XML:2003 section
* 4.8.5.
*/
/*
* Given a query, which must return type oid as first column, produce
* a list of Oids with the query results.
*/
static List *
query_to_oid_list(const char *query)
{
int i;
List *list = NIL;
SPI_execute(query, true, 0);
for (i = 0; i < SPI_processed; i++)
{
Oid oid;
bool isnull;
oid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull));
if (isnull)
continue;
list = lappend_oid(list, oid);
}
return list;
}
static List *
schema_get_xml_visible_tables(Oid nspid)
{
StringInfoData query;
initStringInfo(&query);
appendStringInfo(&query, "SELECT oid FROM pg_class WHERE relnamespace = %u AND relkind IN ('r', 'v') AND has_table_privilege (oid, 'SELECT') ORDER BY relname;", nspid);
return query_to_oid_list(query.data);
}
/*
* Including the system schemas is probably not useful for a database
* mapping.
*/
#define XML_VISIBLE_SCHEMAS_EXCLUDE "nspname LIKE 'pg_%' ESCAPE '' OR nspname = 'information_schema'"
#define XML_VISIBLE_SCHEMAS "SELECT oid FROM pg_namespace WHERE has_schema_privilege (oid, 'USAGE') AND NOT (" XML_VISIBLE_SCHEMAS_EXCLUDE ")"
static List *
database_get_xml_visible_schemas(void)
{
return query_to_oid_list(XML_VISIBLE_SCHEMAS " ORDER BY nspname;");
}
static List *
database_get_xml_visible_tables(void)
{
/* At the moment there is no order required here. */
return query_to_oid_list("SELECT oid FROM pg_class WHERE relkind IN ('r', 'v') AND has_table_privilege (pg_class.oid, 'SELECT') AND relnamespace IN (" XML_VISIBLE_SCHEMAS ");");
}
/* /*
* Map SQL table to XML and/or XML Schema document; see SQL/XML:2003 * Map SQL table to XML and/or XML Schema document; see SQL/XML:2003
* section 9.3. * section 9.3.
*/ */
static StringInfo
table_to_xml_internal(Oid relid, bool nulls, bool tableforest, const char *targetns, bool top_level)
{
StringInfoData query;
initStringInfo(&query);
appendStringInfo(&query, "SELECT * FROM %s", DatumGetCString(DirectFunctionCall1(regclassout, ObjectIdGetDatum(relid))));
return query_to_xml_internal(query.data, get_rel_name(relid), NULL, nulls, tableforest, targetns, top_level);
}
Datum Datum
table_to_xml(PG_FUNCTION_ARGS) table_to_xml(PG_FUNCTION_ARGS)
{ {
...@@ -1670,12 +1787,7 @@ table_to_xml(PG_FUNCTION_ARGS) ...@@ -1670,12 +1787,7 @@ table_to_xml(PG_FUNCTION_ARGS)
bool tableforest = PG_GETARG_BOOL(2); bool tableforest = PG_GETARG_BOOL(2);
const char *targetns = _textout(PG_GETARG_TEXT_P(3)); const char *targetns = _textout(PG_GETARG_TEXT_P(3));
StringInfoData query; PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid, nulls, tableforest, targetns, true)));
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)));
} }
...@@ -1687,7 +1799,7 @@ query_to_xml(PG_FUNCTION_ARGS) ...@@ -1687,7 +1799,7 @@ query_to_xml(PG_FUNCTION_ARGS)
bool tableforest = PG_GETARG_BOOL(2); bool tableforest = PG_GETARG_BOOL(2);
const char *targetns = _textout(PG_GETARG_TEXT_P(3)); 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))); PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL, NULL, nulls, tableforest, targetns, true)));
} }
...@@ -1715,7 +1827,7 @@ cursor_to_xml(PG_FUNCTION_ARGS) ...@@ -1715,7 +1827,7 @@ cursor_to_xml(PG_FUNCTION_ARGS)
SPI_cursor_fetch(portal, true, count); SPI_cursor_fetch(portal, true, count);
for (i = 0; i < SPI_processed; i++) for (i = 0; i < SPI_processed; i++)
SPI_sql_row_to_xmlelement(i, &result, NULL, nulls, tableforest, targetns); SPI_sql_row_to_xmlelement(i, &result, NULL, nulls, tableforest, targetns, true);
SPI_finish(); SPI_finish();
...@@ -1723,8 +1835,52 @@ cursor_to_xml(PG_FUNCTION_ARGS) ...@@ -1723,8 +1835,52 @@ cursor_to_xml(PG_FUNCTION_ARGS)
} }
/*
* Write the start tag of the root element of a data mapping.
*
* top_level means that this is the very top level of the eventual
* output. For example, when the user calls table_to_xml, then a call
* with a table name to this function is the top level. When the user
* calls database_to_xml, then a call with a schema name to this
* function is not the top level. If top_level is false, then the XML
* namespace declarations are omitted, because they supposedly already
* appeared earlier in the output. Repeating them is not wrong, but
* it looks ugly.
*/
static void
xmldata_root_element_start(StringInfo result, const char *eltname, const char *xmlschema, const char *targetns, bool top_level)
{
/* This isn't really wrong but currently makes no sense. */
Assert(top_level || !xmlschema);
appendStringInfo(result, "<%s", eltname);
if (top_level)
{
appendStringInfoString(result, " xmlns:xsi=\"" NAMESPACE_XSI "\"");
if (strlen(targetns) > 0)
appendStringInfo(result, " xmlns=\"%s\"", targetns);
}
if (xmlschema)
{
/* FIXME: better targets */
if (strlen(targetns) > 0)
appendStringInfo(result, " xsi:schemaLocation=\"%s #\"", targetns);
else
appendStringInfo(result, " xsi:noNamespaceSchemaLocation=\"#\"");
}
appendStringInfo(result, ">\n\n");
}
static void
xmldata_root_element_end(StringInfo result, const char *eltname)
{
appendStringInfo(result, "</%s>\n", eltname);
}
static StringInfo static StringInfo
query_to_xml_internal(const char *query, char *tablename, const char *xmlschema, bool nulls, bool tableforest, const char *targetns) query_to_xml_internal(const char *query, char *tablename, const char *xmlschema, bool nulls, bool tableforest, const char *targetns, bool top_level)
{ {
StringInfo result; StringInfo result;
char *xmltn; char *xmltn;
...@@ -1744,30 +1900,16 @@ query_to_xml_internal(const char *query, char *tablename, const char *xmlschema, ...@@ -1744,30 +1900,16 @@ query_to_xml_internal(const char *query, char *tablename, const char *xmlschema,
errmsg("invalid query"))); errmsg("invalid query")));
if (!tableforest) if (!tableforest)
{ xmldata_root_element_start(result, xmltn, xmlschema, targetns, top_level);
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) if (xmlschema)
appendStringInfo(result, "%s\n\n", xmlschema); appendStringInfo(result, "%s\n\n", xmlschema);
for(i = 0; i < SPI_processed; i++) for(i = 0; i < SPI_processed; i++)
SPI_sql_row_to_xmlelement(i, result, tablename, nulls, tableforest, targetns); SPI_sql_row_to_xmlelement(i, result, tablename, nulls, tableforest, targetns, top_level);
if (!tableforest) if (!tableforest)
appendStringInfo(result, "</%s>\n", xmltn); xmldata_root_element_end(result, xmltn);
SPI_finish(); SPI_finish();
...@@ -1861,7 +2003,7 @@ table_to_xml_and_xmlschema(PG_FUNCTION_ARGS) ...@@ -1861,7 +2003,7 @@ table_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
initStringInfo(&query); initStringInfo(&query);
appendStringInfo(&query, "SELECT * FROM %s", DatumGetCString(DirectFunctionCall1(regclassout, ObjectIdGetDatum(relid)))); 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))); PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query.data, get_rel_name(relid), xmlschema, nulls, tableforest, targetns, true)));
} }
...@@ -1884,7 +2026,302 @@ query_to_xml_and_xmlschema(PG_FUNCTION_ARGS) ...@@ -1884,7 +2026,302 @@ query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
SPI_cursor_close(portal); SPI_cursor_close(portal);
SPI_finish(); SPI_finish();
PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL, xmlschema, nulls, tableforest, targetns))); PG_RETURN_XML_P(stringinfo_to_xmltype(query_to_xml_internal(query, NULL, xmlschema, nulls, tableforest, targetns, true)));
}
/*
* Map SQL schema to XML and/or XML Schema document; see SQL/XML:2003
* section 9.4.
*/
static StringInfo
schema_to_xml_internal(Oid nspid, const char *xmlschema, bool nulls, bool tableforest, const char *targetns, bool top_level)
{
StringInfo result;
char *xmlsn;
List *relid_list;
ListCell *cell;
xmlsn = map_sql_identifier_to_xml_name(get_namespace_name(nspid), true, false);
result = makeStringInfo();
xmldata_root_element_start(result, xmlsn, xmlschema, targetns, top_level);
if (xmlschema)
appendStringInfo(result, "%s\n\n", xmlschema);
SPI_connect();
relid_list = schema_get_xml_visible_tables(nspid);
SPI_push();
foreach(cell, relid_list)
{
Oid relid = lfirst_oid(cell);
StringInfo subres;
subres = table_to_xml_internal(relid, nulls, tableforest, targetns, false);
appendStringInfoString(result, subres->data);
appendStringInfoChar(result, '\n');
}
SPI_pop();
SPI_finish();
xmldata_root_element_end(result, xmlsn);
return result;
}
Datum
schema_to_xml(PG_FUNCTION_ARGS)
{
Name name = PG_GETARG_NAME(0);
bool nulls = PG_GETARG_BOOL(1);
bool tableforest = PG_GETARG_BOOL(2);
const char *targetns = _textout(PG_GETARG_TEXT_P(3));
char *schemaname;
Oid nspid;
schemaname = NameStr(*name);
nspid = LookupExplicitNamespace(schemaname);
PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid, NULL, nulls, tableforest, targetns, true)));
}
/*
* Write the start element of the root element of an XML Schema mapping.
*/
static void
xsd_schema_element_start(StringInfo result, const char *targetns)
{
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");
}
static void
xsd_schema_element_end(StringInfo result)
{
appendStringInfoString(result,
"</xsd:schema>");
}
static StringInfo
schema_to_xmlschema_internal(const char *schemaname, bool nulls, bool tableforest, const char *targetns)
{
Oid nspid;
List *relid_list;
List *tupdesc_list;
ListCell *cell;
StringInfo result;
result = makeStringInfo();
nspid = LookupExplicitNamespace(schemaname);
xsd_schema_element_start(result, targetns);
SPI_connect();
relid_list = schema_get_xml_visible_tables(nspid);
tupdesc_list = NIL;
foreach (cell, relid_list)
{
Relation rel;
rel = heap_open(lfirst_oid(cell), AccessShareLock);
tupdesc_list = lappend(tupdesc_list, rel->rd_att);
heap_close(rel, NoLock);
}
appendStringInfoString(result,
map_sql_typecoll_to_xmlschema_types(tupdesc_list));
appendStringInfoString(result,
map_sql_schema_to_xmlschema_types(nspid, relid_list, nulls, tableforest, targetns));
xsd_schema_element_end(result);
SPI_finish();
return result;
}
Datum
schema_to_xmlschema(PG_FUNCTION_ARGS)
{
Name name = PG_GETARG_NAME(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(schema_to_xmlschema_internal(NameStr(*name), nulls, tableforest, targetns)));
}
Datum
schema_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
{
Name name = PG_GETARG_NAME(0);
bool nulls = PG_GETARG_BOOL(1);
bool tableforest = PG_GETARG_BOOL(2);
const char *targetns = _textout(PG_GETARG_TEXT_P(3));
char *schemaname;
Oid nspid;
StringInfo xmlschema;
schemaname = NameStr(*name);
nspid = LookupExplicitNamespace(schemaname);
xmlschema = schema_to_xmlschema_internal(schemaname, nulls, tableforest, targetns);
PG_RETURN_XML_P(stringinfo_to_xmltype(schema_to_xml_internal(nspid, xmlschema->data, nulls, tableforest, targetns, true)));
}
/*
* Map SQL database to XML and/or XML Schema document; see SQL/XML:2003
* section 9.5.
*/
static StringInfo
database_to_xml_internal(const char *xmlschema, bool nulls, bool tableforest, const char *targetns)
{
StringInfo result;
List *nspid_list;
ListCell *cell;
char *xmlcn;
xmlcn = map_sql_identifier_to_xml_name(get_database_name(MyDatabaseId), true, false);
result = makeStringInfo();
xmldata_root_element_start(result, xmlcn, xmlschema, targetns, true);
if (xmlschema)
appendStringInfo(result, "%s\n\n", xmlschema);
SPI_connect();
nspid_list = database_get_xml_visible_schemas();
SPI_push();
foreach(cell, nspid_list)
{
Oid nspid = lfirst_oid(cell);
StringInfo subres;
subres = schema_to_xml_internal(nspid, NULL, nulls, tableforest, targetns, false);
appendStringInfoString(result, subres->data);
appendStringInfoChar(result, '\n');
}
SPI_pop();
SPI_finish();
xmldata_root_element_end(result, xmlcn);
return result;
}
Datum
database_to_xml(PG_FUNCTION_ARGS)
{
bool nulls = PG_GETARG_BOOL(0);
bool tableforest = PG_GETARG_BOOL(1);
const char *targetns = _textout(PG_GETARG_TEXT_P(2));
PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(NULL, nulls, tableforest, targetns)));
}
static StringInfo
database_to_xmlschema_internal(bool nulls, bool tableforest, const char *targetns)
{
List *relid_list;
List *nspid_list;
List *tupdesc_list;
ListCell *cell;
StringInfo result;
result = makeStringInfo();
xsd_schema_element_start(result, targetns);
SPI_connect();
relid_list = database_get_xml_visible_tables();
nspid_list = database_get_xml_visible_schemas();
tupdesc_list = NIL;
foreach (cell, relid_list)
{
Relation rel;
rel = heap_open(lfirst_oid(cell), AccessShareLock);
tupdesc_list = lappend(tupdesc_list, rel->rd_att);
heap_close(rel, NoLock);
}
appendStringInfoString(result,
map_sql_typecoll_to_xmlschema_types(tupdesc_list));
appendStringInfoString(result,
map_sql_catalog_to_xmlschema_types(nspid_list, nulls, tableforest, targetns));
xsd_schema_element_end(result);
SPI_finish();
return result;
}
Datum
database_to_xmlschema(PG_FUNCTION_ARGS)
{
bool nulls = PG_GETARG_BOOL(0);
bool tableforest = PG_GETARG_BOOL(1);
const char *targetns = _textout(PG_GETARG_TEXT_P(2));
PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xmlschema_internal(nulls, tableforest, targetns)));
}
Datum
database_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
{
bool nulls = PG_GETARG_BOOL(0);
bool tableforest = PG_GETARG_BOOL(1);
const char *targetns = _textout(PG_GETARG_TEXT_P(2));
StringInfo xmlschema;
xmlschema = database_to_xmlschema_internal(nulls, tableforest, targetns);
PG_RETURN_XML_P(stringinfo_to_xmltype(database_to_xml_internal(xmlschema->data, nulls, tableforest, targetns)));
} }
...@@ -1960,20 +2397,10 @@ map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls, bool tablef ...@@ -1960,20 +2397,10 @@ map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls, bool tablef
rowtypename = "RowType"; rowtypename = "RowType";
} }
appendStringInfoString(&result, xsd_schema_element_start(&result, targetns);
"<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, appendStringInfoString(&result,
map_sql_typecoll_to_xmlschema_types(tupdesc)); map_sql_typecoll_to_xmlschema_types(list_make1(tupdesc)));
appendStringInfo(&result, appendStringInfo(&result,
"<xsd:complexType name=\"%s\">\n" "<xsd:complexType name=\"%s\">\n"
...@@ -2010,8 +2437,126 @@ map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls, bool tablef ...@@ -2010,8 +2437,126 @@ map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls, bool tablef
"<xsd:element name=\"%s\" type=\"%s\"/>\n\n", "<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
xmltn, rowtypename); xmltn, rowtypename);
xsd_schema_element_end(&result);
return result.data;
}
/*
* Map an SQL schema to XML Schema data types; see SQL/XML section
* 9.7.
*/
static const char *
map_sql_schema_to_xmlschema_types(Oid nspid, List *relid_list, bool nulls, bool tableforest, const char *targetns)
{
char *xmlsn;
char *schematypename;
StringInfoData result;
ListCell *cell;
initStringInfo(&result);
xmlsn = map_sql_identifier_to_xml_name(get_namespace_name(nspid), true, false);
schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
get_database_name(MyDatabaseId),
get_namespace_name(nspid),
NULL);
appendStringInfo(&result,
"<xsd:complexType name=\"%s\">\n", schematypename);
if (!tableforest)
appendStringInfoString(&result,
" <xsd:all>\n");
else
appendStringInfoString(&result,
" <xsd:sequence>\n");
foreach (cell, relid_list)
{
Oid relid = lfirst_oid(cell);
char *xmltn = map_sql_identifier_to_xml_name(get_rel_name(relid), true, false);
char *tabletypename = map_multipart_sql_identifier_to_xml_name(tableforest ? "RowType" : "TableType",
get_database_name(MyDatabaseId),
get_namespace_name(nspid),
get_rel_name(relid));
if (!tableforest)
appendStringInfo(&result,
" <xsd:element name=\"%s\" type=\"%s\" />\n",
xmltn, tabletypename);
else
appendStringInfo(&result,
" <xsd:element name=\"%s\" type=\"%s\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n",
xmltn, tabletypename);
}
if (!tableforest)
appendStringInfoString(&result,
" </xsd:all>\n");
else
appendStringInfoString(&result,
" </xsd:sequence>\n");
appendStringInfoString(&result, appendStringInfoString(&result,
"</xsd:schema>"); "</xsd:complexType>\n\n");
appendStringInfo(&result,
"<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
xmlsn, schematypename);
return result.data;
}
/*
* Map an SQL catalog to XML Schema data types; see SQL/XML section
* 9.8.
*/
static const char *
map_sql_catalog_to_xmlschema_types(List *nspid_list, bool nulls, bool tableforest, const char *targetns)
{
char *xmlcn;
char *catalogtypename;
StringInfoData result;
ListCell *cell;
initStringInfo(&result);
xmlcn = map_sql_identifier_to_xml_name(get_database_name(MyDatabaseId), true, false);
catalogtypename = map_multipart_sql_identifier_to_xml_name("CatalogType",
get_database_name(MyDatabaseId),
NULL,
NULL);
appendStringInfo(&result,
"<xsd:complexType name=\"%s\">\n", catalogtypename);
appendStringInfoString(&result,
" <xsd:all>\n");
foreach (cell, nspid_list)
{
Oid nspid = lfirst_oid(cell);
char *xmlsn = map_sql_identifier_to_xml_name(get_namespace_name(nspid), true, false);
char *schematypename = map_multipart_sql_identifier_to_xml_name("SchemaType",
get_database_name(MyDatabaseId),
get_namespace_name(nspid),
NULL);
appendStringInfo(&result,
" <xsd:element name=\"%s\" type=\"%s\" />\n",
xmlsn, schematypename);
}
appendStringInfoString(&result,
" </xsd:all>\n");
appendStringInfoString(&result,
"</xsd:complexType>\n\n");
appendStringInfo(&result,
"<xsd:element name=\"%s\" type=\"%s\"/>\n\n",
xmlcn, catalogtypename);
return result.data; return result.data;
} }
...@@ -2121,41 +2666,41 @@ map_sql_type_to_xml_name(Oid typeoid, int typmod) ...@@ -2121,41 +2666,41 @@ map_sql_type_to_xml_name(Oid typeoid, int typmod)
* SQL/XML:2002 section 9.10. * SQL/XML:2002 section 9.10.
*/ */
static const char * static const char *
map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc) map_sql_typecoll_to_xmlschema_types(List *tupdesc_list)
{ {
Oid *uniquetypes; List *uniquetypes = NIL;
int i, j; int i;
int len;
StringInfoData result; StringInfoData result;
ListCell *cell0, *cell1, *cell2;
initStringInfo(&result); foreach (cell0, tupdesc_list)
uniquetypes = palloc(2 * sizeof(*uniquetypes) * tupdesc->natts);
len = 0;
for (i = 1; i <= tupdesc->natts; i++)
{ {
bool already_done = false; TupleDesc tupdesc = lfirst(cell0);
Oid type = SPI_gettypeid(tupdesc, i);
for (j = 0; j < len; j++) for (i = 1; i <= tupdesc->natts; i++)
if (type == uniquetypes[j]) {
{ bool already_done = false;
already_done = true; Oid type = SPI_gettypeid(tupdesc, i);
break; foreach (cell1, uniquetypes)
} if (type == lfirst_oid(cell1))
if (already_done) {
continue; already_done = true;
break;
}
if (already_done)
continue;
uniquetypes[len++] = type; uniquetypes = lappend_oid(uniquetypes, type);
}
} }
/* add base types of domains */ /* add base types of domains */
for (i = 0; i < len; i++) foreach (cell1, uniquetypes)
{ {
bool already_done = false; bool already_done = false;
Oid type = getBaseType(uniquetypes[i]); Oid type = getBaseType(lfirst_oid(cell1));
for (j = 0; j < len; j++) foreach (cell2, uniquetypes)
if (type == uniquetypes[j]) if (type == lfirst_oid(cell2))
{ {
already_done = true; already_done = true;
break; break;
...@@ -2163,11 +2708,13 @@ map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc) ...@@ -2163,11 +2708,13 @@ map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc)
if (already_done) if (already_done)
continue; continue;
uniquetypes[len++] = type; uniquetypes = lappend_oid(uniquetypes, type);
} }
for (i = 0; i < len; i++) initStringInfo(&result);
appendStringInfo(&result, "%s\n", map_sql_type_to_xmlschema_type(uniquetypes[i], -1));
foreach (cell1, uniquetypes)
appendStringInfo(&result, "%s\n", map_sql_type_to_xmlschema_type(lfirst_oid(cell1), -1));
return result.data; return result.data;
} }
...@@ -2178,7 +2725,7 @@ map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc) ...@@ -2178,7 +2725,7 @@ map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc)
* sections 9.11 and 9.15. * sections 9.11 and 9.15.
* *
* (The distinction between 9.11 and 9.15 is basically that 9.15 adds * (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 * a name attribute, which this function does. The name-less version
* 9.11 doesn't appear to be required anywhere.) * 9.11 doesn't appear to be required anywhere.)
*/ */
static const char * static const char *
...@@ -2355,7 +2902,7 @@ map_sql_type_to_xmlschema_type(Oid typeoid, int typmod) ...@@ -2355,7 +2902,7 @@ map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
* SPI cursor. See also SQL/XML:2003 section 9.12. * SPI cursor. See also SQL/XML:2003 section 9.12.
*/ */
static void static void
SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns) SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns, bool top_level)
{ {
int i; int i;
char *xmltn; char *xmltn;
...@@ -2371,12 +2918,7 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool n ...@@ -2371,12 +2918,7 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool n
} }
if (tableforest) if (tableforest)
{ xmldata_root_element_start(result, xmltn, NULL, targetns, top_level);
appendStringInfo(result, "<%s xmlns:xsi=\"" NAMESPACE_XSI "\"", xmltn);
if (strlen(targetns) > 0)
appendStringInfo(result, " xmlns=\"%s\"", targetns);
appendStringInfo(result, ">\n");
}
else else
appendStringInfoString(result, "<row>\n"); appendStringInfoString(result, "<row>\n");
...@@ -2402,7 +2944,10 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool n ...@@ -2402,7 +2944,10 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool n
} }
if (tableforest) if (tableforest)
appendStringInfo(result, "</%s>\n\n", xmltn); {
xmldata_root_element_end(result, xmltn);
appendStringInfoChar(result, '\n');
}
else else
appendStringInfoString(result, "</row>\n\n"); appendStringInfoString(result, "</row>\n\n");
} }
......
...@@ -7,7 +7,7 @@ ...@@ -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.452 2007/03/30 18:34:56 mha Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.453 2007/04/01 09:00:25 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
...@@ -4100,6 +4100,20 @@ DESCR("map table contents and structure to XML and XML Schema"); ...@@ -4100,6 +4100,20 @@ DESCR("map table contents and structure to XML and XML Schema");
DATA(insert OID = 2930 ( 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_ )); DATA(insert OID = 2930 ( 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"); DESCR("map query result and structure to XML and XML Schema");
DATA(insert OID = 2933 ( schema_to_xml PGNSP PGUID 12 100 0 f f t f s 4 142 "19 16 16 25" _null_ _null_ "{schema,nulls,tableforest,targetns}" schema_to_xml - _null_ ));
DESCR("map schema contents to XML");
DATA(insert OID = 2934 ( schema_to_xmlschema PGNSP PGUID 12 100 0 f f t f s 4 142 "19 16 16 25" _null_ _null_ "{schema,nulls,tableforest,targetns}" schema_to_xmlschema - _null_ ));
DESCR("map schema structure to XML Schema");
DATA(insert OID = 2935 ( schema_to_xml_and_xmlschema PGNSP PGUID 12 100 0 f f t f s 4 142 "19 16 16 25" _null_ _null_ "{schema,nulls,tableforest,targetns}" schema_to_xml_and_xmlschema - _null_ ));
DESCR("map schema contents and structure to XML and XML Schema");
DATA(insert OID = 2936 ( database_to_xml PGNSP PGUID 12 100 0 f f t f s 3 142 "16 16 25" _null_ _null_ "{nulls,tableforest,targetns}" database_to_xml - _null_ ));
DESCR("map database contents to XML");
DATA(insert OID = 2937 ( database_to_xmlschema PGNSP PGUID 12 100 0 f f t f s 3 142 "16 16 25" _null_ _null_ "{nulls,tableforest,targetns}" database_to_xmlschema - _null_ ));
DESCR("map database structure to XML Schema");
DATA(insert OID = 2938 ( database_to_xml_and_xmlschema PGNSP PGUID 12 100 0 f f t f s 3 142 "16 16 25" _null_ _null_ "{nulls,tableforest,targetns}" database_to_xml_and_xmlschema - _null_ ));
DESCR("map database contents and structure to XML and XML Schema");
DATA(insert OID = 2931 ( xmlpath PGNSP PGUID 12 1 0 f f f f i 3 143 "25 142 1009" _null_ _null_ _null_ xmlpath - _null_ )); DATA(insert OID = 2931 ( xmlpath PGNSP PGUID 12 1 0 f f f f i 3 143 "25 142 1009" _null_ _null_ _null_ xmlpath - _null_ ));
DESCR("evaluate XPath expression, with namespaces support"); DESCR("evaluate XPath expression, with namespaces support");
DATA(insert OID = 2932 ( xmlpath PGNSP PGUID 14 1 0 f f f f i 2 143 "25 142" _null_ _null_ _null_ "select pg_catalog.xmlpath($1, $2, NULL)" - _null_ )); DATA(insert OID = 2932 ( xmlpath PGNSP PGUID 14 1 0 f f f f i 2 143 "25 142" _null_ _null_ _null_ "select pg_catalog.xmlpath($1, $2, NULL)" - _null_ ));
......
...@@ -7,7 +7,7 @@ ...@@ -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.17 2007/03/22 20:14:58 momjian Exp $ * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.18 2007/04/01 09:00:26 petere Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -47,6 +47,14 @@ extern Datum cursor_to_xmlschema(PG_FUNCTION_ARGS); ...@@ -47,6 +47,14 @@ extern Datum cursor_to_xmlschema(PG_FUNCTION_ARGS);
extern Datum table_to_xml_and_xmlschema(PG_FUNCTION_ARGS); extern Datum table_to_xml_and_xmlschema(PG_FUNCTION_ARGS);
extern Datum query_to_xml_and_xmlschema(PG_FUNCTION_ARGS); extern Datum query_to_xml_and_xmlschema(PG_FUNCTION_ARGS);
extern Datum schema_to_xml(PG_FUNCTION_ARGS);
extern Datum schema_to_xmlschema(PG_FUNCTION_ARGS);
extern Datum schema_to_xml_and_xmlschema(PG_FUNCTION_ARGS);
extern Datum database_to_xml(PG_FUNCTION_ARGS);
extern Datum database_to_xmlschema(PG_FUNCTION_ARGS);
extern Datum database_to_xml_and_xmlschema(PG_FUNCTION_ARGS);
typedef enum typedef enum
{ {
XML_STANDALONE_YES, XML_STANDALONE_YES,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment