OSDN Git Service

Mapping schemas and databases to XML and XML Schema.
authorPeter Eisentraut <peter_e@gmx.net>
Sun, 1 Apr 2007 09:00:26 +0000 (09:00 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Sun, 1 Apr 2007 09:00:26 +0000 (09:00 +0000)
Refactor and document the remaining mapping code.

doc/src/sgml/func.sgml
src/backend/utils/adt/xml.c
src/include/catalog/pg_proc.h
src/include/utils/xml.h

index f800155..db7cd1d 100644 (file)
@@ -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
@@ -11531,6 +11531,65 @@ query_to_xml_and_xmlschema(query text, nulls boolean, tableforest boolean, targe
    </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
     converts the output of
index e9c9496..55be759 100644 (file)
@@ -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;
@@ -1658,10 +1660,125 @@ _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);
+
+               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[len++] = type;
+                       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");
 }
index 713d76f..252d746 100644 (file)
@@ -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_ ));
index ef06e41..15ba500 100644 (file)
@@ -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,