Mapping schemas and databases to XML and XML Schema.
authorPeter Eisentraut
Sun, 1 Apr 2007 09:00:26 +0000 (09:00 +0000)
committerPeter Eisentraut
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 f8001551e695bcb176ba7001337d70c09619d4f5..db7cd1d1f3b3a1d6c3de246f5f5cc5128ed9870d 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
  
   Functions and Operators
@@ -11427,7 +11427,7 @@ cursor_to_xml(cursor refcursor, count int, nulls boolean, tableforest boolean, t
     query whose text is passed as parameter
     query and maps the result set.
     cursor_to_xml fetches the indicated number of
-    rows from the cursor specificed by the parameter
+    rows from the cursor specified by the parameter
     cursor.  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
 
-
+name xsi:nil="true"/>
 ]]>
     where xsi 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
 
    
 
+   
+    In addition, the following functions are available to produce
+    analogous mappings of entire schemas or the entire current
+    database.
+
+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)
+
+
+    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.
+   
+
+   
+    The result of a schema content mapping looks like this:
+
+
+
+
+table1-mapping
+
+table2-mapping
+
+...
+
+]]>
+
+    where the format of a table mapping depends on the
+    tableforest parameter as explained above.
+   
+
+   
+    The result of a database content mapping looks like this:
+
+
+
+
+
+  ...
+
+
+
+  ...
+
+
+...
+
+]]>
+
+    where the schema mapping is as above.
+   
+
    
     As an example for using the output produced by these functions,
      shows an XSLT stylesheet that
index e9c94968011bb1c69531c03f907914d6b6ff00cf..55be759c9e331bdfd2a8cdfb58639d90b2f209c8 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;
@@ -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, "\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, "\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,
+                          "
+                          "    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,
+                          "");
+}
+
+
+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,
-                          "
-                          "    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,
                     "\n"
@@ -2010,8 +2437,126 @@ map_sql_table_to_xmlschema(TupleDesc tupdesc, Oid relid, bool nulls, bool tablef
                         "\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,
+                    "\n", schematypename);
+   if (!tableforest)
+       appendStringInfoString(&result,
+                              "  \n");
+   else
+       appendStringInfoString(&result,
+                              "  \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,
+                            "    \n",
+                            xmltn, tabletypename);
+       else
+           appendStringInfo(&result,
+                            "    \n",
+                            xmltn, tabletypename);
+   }
+
+   if (!tableforest)
+       appendStringInfoString(&result,
+                              "  \n");
+   else
+       appendStringInfoString(&result,
+                              "  \n");
    appendStringInfoString(&result,
-                          "");
+                          "\n\n");
+
+   appendStringInfo(&result,
+                    "\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,
+                    "\n", catalogtypename);
+   appendStringInfoString(&result,
+                          "  \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,
+                        "    \n",
+                        xmlsn, schematypename);
+   }
+
+   appendStringInfoString(&result,
+                          "  \n");
+   appendStringInfoString(&result,
+                          "\n\n");
+
+   appendStringInfo(&result,
+                    "\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, "\n");
 
@@ -2402,7 +2944,10 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool n
    }
 
    if (tableforest)
-       appendStringInfo(result, "\n\n", xmltn);
+   {
+       xmldata_root_element_end(result, xmltn);
+       appendStringInfoChar(result, '\n');
+   }
    else
        appendStringInfoString(result, "\n\n");
 }
index 713d76fbfaccc13a47e104665f04ba1cb54446a4..252d7460ac7658d18839f84deea29480f6f9b45c 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 ef06e41b92bfc8e6070e48b25a68be6fbc3b5ef2..15ba500d5078654eb8b66a984ceca67e1083138b 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,