Mapping schemas and databases to XML and XML Schema.
Refactor and document the remaining mapping code.
This commit is contained in:
parent
ae8072a404
commit
0b75afda92
@ -1,4 +1,4 @@
|
||||
<!-- $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">
|
||||
<title>Functions and Operators</title>
|
||||
@ -11427,7 +11427,7 @@ cursor_to_xml(cursor refcursor, count int, nulls boolean, tableforest boolean, t
|
||||
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
|
||||
rows from the cursor specified 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.
|
||||
@ -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
|
||||
columns are represented as
|
||||
<screen><![CDATA[
|
||||
<columname xsi:nil="true"/>
|
||||
<columnname xsi:nil="true"/>
|
||||
]]></screen>
|
||||
where <literal>xsi</literal> is the XML namespace prefix for XML
|
||||
Schema Instance. An appropriate namespace declaration will be
|
||||
@ -11530,6 +11530,65 @@ query_to_xml_and_xmlschema(query text, nulls boolean, tableforest boolean, targe
|
||||
</synopsis>
|
||||
</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>
|
||||
As an example for using the output produced by these functions,
|
||||
<xref linkend="xslt-xml-html"> shows an XSLT stylesheet that
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.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);
|
||||
|
||||
#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_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_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 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;
|
||||
@ -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
|
||||
* 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
|
||||
table_to_xml(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@ -1670,12 +1787,7 @@ table_to_xml(PG_FUNCTION_ARGS)
|
||||
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)));
|
||||
PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid, nulls, tableforest, targetns, true)));
|
||||
}
|
||||
|
||||
|
||||
@ -1687,7 +1799,7 @@ query_to_xml(PG_FUNCTION_ARGS)
|
||||
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)));
|
||||
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)
|
||||
|
||||
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_sql_row_to_xmlelement(i, &result, NULL, nulls, tableforest, targetns, true);
|
||||
|
||||
SPI_finish();
|
||||
|
||||
@ -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
|
||||
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;
|
||||
char *xmltn;
|
||||
@ -1744,30 +1900,16 @@ query_to_xml_internal(const char *query, char *tablename, const char *xmlschema,
|
||||
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");
|
||||
}
|
||||
xmldata_root_element_start(result, xmltn, xmlschema, targetns, top_level);
|
||||
|
||||
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);
|
||||
SPI_sql_row_to_xmlelement(i, result, tablename, nulls, tableforest, targetns, top_level);
|
||||
|
||||
if (!tableforest)
|
||||
appendStringInfo(result, "</%s>\n", xmltn);
|
||||
xmldata_root_element_end(result, xmltn);
|
||||
|
||||
SPI_finish();
|
||||
|
||||
@ -1861,7 +2003,7 @@ table_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
|
||||
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)));
|
||||
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)
|
||||
SPI_cursor_close(portal);
|
||||
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
|
||||
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");
|
||||
xsd_schema_element_start(&result, targetns);
|
||||
|
||||
appendStringInfoString(&result,
|
||||
map_sql_typecoll_to_xmlschema_types(tupdesc));
|
||||
map_sql_typecoll_to_xmlschema_types(list_make1(tupdesc)));
|
||||
|
||||
appendStringInfo(&result,
|
||||
"<xsd:complexType name=\"%s\">\n"
|
||||
@ -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",
|
||||
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,
|
||||
"</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;
|
||||
}
|
||||
@ -2121,41 +2666,41 @@ map_sql_type_to_xml_name(Oid typeoid, int typmod)
|
||||
* SQL/XML:2002 section 9.10.
|
||||
*/
|
||||
static const char *
|
||||
map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc)
|
||||
map_sql_typecoll_to_xmlschema_types(List *tupdesc_list)
|
||||
{
|
||||
Oid *uniquetypes;
|
||||
int i, j;
|
||||
int len;
|
||||
List *uniquetypes = NIL;
|
||||
int i;
|
||||
StringInfoData result;
|
||||
ListCell *cell0, *cell1, *cell2;
|
||||
|
||||
initStringInfo(&result);
|
||||
|
||||
uniquetypes = palloc(2 * sizeof(*uniquetypes) * tupdesc->natts);
|
||||
len = 0;
|
||||
|
||||
for (i = 1; i <= tupdesc->natts; i++)
|
||||
foreach (cell0, tupdesc_list)
|
||||
{
|
||||
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;
|
||||
TupleDesc tupdesc = lfirst(cell0);
|
||||
|
||||
uniquetypes[len++] = type;
|
||||
for (i = 1; i <= tupdesc->natts; i++)
|
||||
{
|
||||
bool already_done = false;
|
||||
Oid type = SPI_gettypeid(tupdesc, i);
|
||||
foreach (cell1, uniquetypes)
|
||||
if (type == lfirst_oid(cell1))
|
||||
{
|
||||
already_done = true;
|
||||
break;
|
||||
}
|
||||
if (already_done)
|
||||
continue;
|
||||
|
||||
uniquetypes = lappend_oid(uniquetypes, type);
|
||||
}
|
||||
}
|
||||
|
||||
/* add base types of domains */
|
||||
for (i = 0; i < len; i++)
|
||||
foreach (cell1, uniquetypes)
|
||||
{
|
||||
bool already_done = false;
|
||||
Oid type = getBaseType(uniquetypes[i]);
|
||||
for (j = 0; j < len; j++)
|
||||
if (type == uniquetypes[j])
|
||||
Oid type = getBaseType(lfirst_oid(cell1));
|
||||
foreach (cell2, uniquetypes)
|
||||
if (type == lfirst_oid(cell2))
|
||||
{
|
||||
already_done = true;
|
||||
break;
|
||||
@ -2163,11 +2708,13 @@ map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc)
|
||||
if (already_done)
|
||||
continue;
|
||||
|
||||
uniquetypes[len++] = type;
|
||||
uniquetypes = lappend_oid(uniquetypes, type);
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
appendStringInfo(&result, "%s\n", map_sql_type_to_xmlschema_type(uniquetypes[i], -1));
|
||||
initStringInfo(&result);
|
||||
|
||||
foreach (cell1, uniquetypes)
|
||||
appendStringInfo(&result, "%s\n", map_sql_type_to_xmlschema_type(lfirst_oid(cell1), -1));
|
||||
|
||||
return result.data;
|
||||
}
|
||||
@ -2178,7 +2725,7 @@ map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc)
|
||||
* 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
|
||||
* a name attribute, which this function does. The name-less version
|
||||
* 9.11 doesn't appear to be required anywhere.)
|
||||
*/
|
||||
static const char *
|
||||
@ -2355,7 +2902,7 @@ map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
|
||||
* 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)
|
||||
SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns, bool top_level)
|
||||
{
|
||||
int i;
|
||||
char *xmltn;
|
||||
@ -2371,12 +2918,7 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool n
|
||||
}
|
||||
|
||||
if (tableforest)
|
||||
{
|
||||
appendStringInfo(result, "<%s xmlns:xsi=\"" NAMESPACE_XSI "\"", xmltn);
|
||||
if (strlen(targetns) > 0)
|
||||
appendStringInfo(result, " xmlns=\"%s\"", targetns);
|
||||
appendStringInfo(result, ">\n");
|
||||
}
|
||||
xmldata_root_element_start(result, xmltn, NULL, targetns, top_level);
|
||||
else
|
||||
appendStringInfoString(result, "<row>\n");
|
||||
|
||||
@ -2402,7 +2944,10 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool n
|
||||
}
|
||||
|
||||
if (tableforest)
|
||||
appendStringInfo(result, "</%s>\n\n", xmltn);
|
||||
{
|
||||
xmldata_root_element_end(result, xmltn);
|
||||
appendStringInfoChar(result, '\n');
|
||||
}
|
||||
else
|
||||
appendStringInfoString(result, "</row>\n\n");
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/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
|
||||
* 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");
|
||||
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");
|
||||
|
||||
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_ ));
|
||||
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_ ));
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/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);
|
||||
extern Datum table_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
|
||||
{
|
||||
XML_STANDALONE_YES,
|
||||
|
Loading…
x
Reference in New Issue
Block a user