diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index f8001551e69..db7cd1d1f3b 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -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 + ]]> 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 diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index e9c94968011..55be759c9e3 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, "\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, + " 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, - " 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); - uniquetypes[len++] = type; + for (i = 1; i <= tupdesc->natts; i++) + { + bool already_done = false; + Oid type = SPI_gettypeid(tupdesc, i); + foreach (cell1, uniquetypes) + if (type == lfirst_oid(cell1)) + { + already_done = true; + break; + } + if (already_done) + continue; + + uniquetypes = lappend_oid(uniquetypes, type); + } } /* add base types of domains */ - for (i = 0; i < len; i++) + foreach (cell1, uniquetypes) { bool already_done = false; - Oid type = getBaseType(uniquetypes[i]); - for (j = 0; j < len; j++) - if (type == uniquetypes[j]) + Oid type = getBaseType(lfirst_oid(cell1)); + foreach (cell2, uniquetypes) + if (type == lfirst_oid(cell2)) { already_done = true; break; @@ -2163,11 +2708,13 @@ map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc) if (already_done) continue; - uniquetypes[len++] = type; + uniquetypes = lappend_oid(uniquetypes, type); } - for (i = 0; i < len; i++) - appendStringInfo(&result, "%s\n", map_sql_type_to_xmlschema_type(uniquetypes[i], -1)); + initStringInfo(&result); + + foreach (cell1, uniquetypes) + appendStringInfo(&result, "%s\n", map_sql_type_to_xmlschema_type(lfirst_oid(cell1), -1)); return result.data; } @@ -2178,7 +2725,7 @@ map_sql_typecoll_to_xmlschema_types(TupleDesc tupdesc) * sections 9.11 and 9.15. * * (The distinction between 9.11 and 9.15 is basically that 9.15 adds - * a name attribute, which thsi function does. The name-less version + * a name attribute, which this function does. The name-less version * 9.11 doesn't appear to be required anywhere.) */ static const char * @@ -2355,7 +2902,7 @@ map_sql_type_to_xmlschema_type(Oid typeoid, int typmod) * SPI cursor. See also SQL/XML:2003 section 9.12. */ static void -SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns) +SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool nulls, bool tableforest, const char *targetns, bool top_level) { int i; char *xmltn; @@ -2371,12 +2918,7 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, bool n } if (tableforest) - { - appendStringInfo(result, "<%s xmlns:xsi=\"" NAMESPACE_XSI "\"", xmltn); - if (strlen(targetns) > 0) - appendStringInfo(result, " xmlns=\"%s\"", targetns); - appendStringInfo(result, ">\n"); - } + xmldata_root_element_start(result, xmltn, NULL, targetns, top_level); else appendStringInfoString(result, "\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"); } diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 713d76fbfac..252d7460ac7 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 ef06e41b92b..15ba500d507 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,