diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index f8001551e695bcb176ba7001337d70c09619d4f5..db7cd1d1f3b3a1d6c3de246f5f5cc5128ed9870d 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -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 diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index e9c94968011bb1c69531c03f907914d6b6ff00cf..55be759c9e331bdfd2a8cdfb58639d90b2f209c8 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -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); + + 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"); } diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 713d76fbfaccc13a47e104665f04ba1cb54446a4..252d7460ac7658d18839f84deea29480f6f9b45c 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -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_ )); diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h index ef06e41b92bfc8e6070e48b25a68be6fbc3b5ef2..15ba500d5078654eb8b66a984ceca67e1083138b 100644 --- a/src/include/utils/xml.h +++ b/src/include/utils/xml.h @@ -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,