📄 xml.c
字号:
} pfree(elem_values); pfree(elem_nulls); } else { Oid typeOut; bool isvarlena; char *p, *str; /* * Special XSD formatting for some data types */ switch (type) { case BOOLOID: if (DatumGetBool(value)) return "true"; else return "false"; case DATEOID: { DateADT date; struct pg_tm tm; char buf[MAXDATELEN + 1]; date = DatumGetDateADT(value); j2date(date + POSTGRES_EPOCH_JDATE, &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday)); EncodeDateOnly(&tm, USE_XSD_DATES, buf); return pstrdup(buf); } case TIMESTAMPOID: { Timestamp timestamp; struct pg_tm tm; fsec_t fsec; char *tzn = NULL; char buf[MAXDATELEN + 1]; timestamp = DatumGetTimestamp(value); /* XSD doesn't support infinite values */ if (TIMESTAMP_NOT_FINITE(timestamp)) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0) EncodeDateTime(&tm, fsec, NULL, &tzn, USE_XSD_DATES, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); return pstrdup(buf); } case TIMESTAMPTZOID: { TimestampTz timestamp; struct pg_tm tm; int tz; fsec_t fsec; char *tzn = NULL; char buf[MAXDATELEN + 1]; timestamp = DatumGetTimestamp(value); /* XSD doesn't support infinite values */ if (TIMESTAMP_NOT_FINITE(timestamp)) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0) EncodeDateTime(&tm, fsec, &tz, &tzn, USE_XSD_DATES, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); return pstrdup(buf); } } getTypeOutputInfo(type, &typeOut, &isvarlena); str = OidOutputFunctionCall(typeOut, value); if (type == XMLOID) return str;#ifdef USE_LIBXML if (type == BYTEAOID) { xmlBufferPtr buf; xmlTextWriterPtr writer; char *result; xml_init(); buf = xmlBufferCreate(); writer = xmlNewTextWriterMemory(buf, 0); if (xmlbinary == XMLBINARY_BASE64) xmlTextWriterWriteBase64(writer, VARDATA(value), 0, VARSIZE(value) - VARHDRSZ); else xmlTextWriterWriteBinHex(writer, VARDATA(value), 0, VARSIZE(value) - VARHDRSZ); xmlFreeTextWriter(writer); result = pstrdup((const char *) xmlBufferContent(buf)); xmlBufferFree(buf); return result; }#endif /* USE_LIBXML */ for (p = str; *p; p += pg_mblen(p)) { switch (*p) { case '&': appendStringInfo(&buf, "&"); break; case '<': appendStringInfo(&buf, "<"); break; case '>': appendStringInfo(&buf, ">"); break; case '\r': appendStringInfo(&buf, "
"); break; default: appendBinaryStringInfo(&buf, p, pg_mblen(p)); break; } } } return buf.data;}static char *_SPI_strdup(const char *s){ size_t len = strlen(s) + 1; char *ret = SPI_palloc(len); memcpy(ret, s, len); return ret;}/* * 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++) { Datum oid; bool isnull; oid = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull); if (!isnull) list = lappend_oid(list, DatumGetObjectId(oid)); } return list;}static List *schema_get_xml_visible_tables(Oid nspid){ StringInfoData query; initStringInfo(&query); appendStringInfo(&query, "SELECT oid FROM pg_catalog.pg_class WHERE relnamespace = %u AND relkind IN ('r', 'v') AND pg_catalog.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 ~ '^pg_' OR nspname = 'information_schema')"#define XML_VISIBLE_SCHEMAS "SELECT oid FROM pg_catalog.pg_namespace WHERE pg_catalog.has_schema_privilege (oid, 'USAGE') AND NOT " XML_VISIBLE_SCHEMAS_EXCLUDEstatic 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_catalog.pg_class WHERE relkind IN ('r', 'v') AND pg_catalog.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 StringInfotable_to_xml_internal(Oid relid, const char *xmlschema, 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), xmlschema, nulls, tableforest, targetns, top_level);}Datumtable_to_xml(PG_FUNCTION_ARGS){ Oid relid = PG_GETARG_OID(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(table_to_xml_internal(relid, NULL, nulls, tableforest, targetns, true)));}Datumquery_to_xml(PG_FUNCTION_ARGS){ char *query = _textout(PG_GETARG_TEXT_P(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(query_to_xml_internal(query, NULL, NULL, nulls, tableforest, targetns, true)));}Datumcursor_to_xml(PG_FUNCTION_ARGS){ char *name = _textout(PG_GETARG_TEXT_P(0)); int32 count = PG_GETARG_INT32(1); bool nulls = PG_GETARG_BOOL(2); bool tableforest = PG_GETARG_BOOL(3); const char *targetns = _textout(PG_GETARG_TEXT_P(4)); StringInfoData result; Portal portal; int i; initStringInfo(&result); SPI_connect(); portal = SPI_cursor_find(name); if (portal == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_CURSOR), errmsg("cursor \"%s\" does not exist", name))); SPI_cursor_fetch(portal, true, count); for (i = 0; i < SPI_processed; i++) SPI_sql_row_to_xmlelement(i, &result, NULL, nulls, tableforest, targetns, true); SPI_finish(); PG_RETURN_XML_P(stringinfo_to_xmltype(&result));}/* * 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 voidxmldata_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 voidxmldata_root_element_end(StringInfo result, const char *eltname){ appendStringInfo(result, "</%s>\n", eltname);}static StringInfoquery_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; int i; if (tablename) xmltn = map_sql_identifier_to_xml_name(tablename, true, false); else xmltn = "table"; result = makeStringInfo(); SPI_connect(); if (SPI_execute(query, true, 0) != SPI_OK_SELECT) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid query"))); if (!tableforest) 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, top_level); if (!tableforest) xmldata_root_element_end(result, xmltn); SPI_finish(); return result;}Datumtable_to_xmlschema(PG_FUNCTION_ARGS){ Oid relid = PG_GETARG_OID(0); bool nulls = PG_GETARG_BOOL(1); bool tableforest = PG_GETARG_BOOL(2); const char *targetns = _textout(PG_GETARG_TEXT_P(3)); const char *result; Relation rel; rel = heap_open(relid, AccessShareLock); result = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls, tableforest, targetns); heap_close(rel, NoLock); PG_RETURN_XML_P(cstring_to_xmltype(result));}Datumquery_to_xmlschema(PG_FUNCTION_ARGS){ char *query = _textout(PG_GETARG_TEXT_P(0)); bool nulls = PG_GETARG_BOOL(1); bool tableforest = PG_GETARG_BOOL(2); const char *targetns = _textout(PG_GETARG_TEXT_P(3)); const char *result; SPIPlanPtr plan; Portal portal; SPI_connect(); if ((plan = SPI_prepare(query, 0, NULL)) == NULL) elog(ERROR, "SPI_prepare(\"%s\") failed", query); if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL) elog(ERROR, "SPI_cursor_open(\"%s\") failed", query); result = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc, InvalidOid, nulls, tableforest, targetns)); SPI_cursor_close(portal); SPI_finish(); PG_RETURN_XML_P(cstring_to_xmltype(result));}Datumcursor_to_xmlschema(PG_FUNCTION_ARGS){ char *name = _textout(PG_GETARG_TEXT_P(0)); bool nulls = PG_GETARG_BOOL(1); bool tableforest = PG_GETARG_BOOL(2); const char *targetns = _textout(PG_GETARG_TEXT_P(3)); const char *xmlschema; Portal portal; SPI_connect(); portal = SPI_cursor_find(name); if (portal == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_CURSOR), errmsg("cursor \"%s\" does not exist", name))); xmlschema = _SPI_strdup(map_sql_table_to_xmlschema(portal->tupDesc, InvalidOid, nulls, tableforest, targetns)); SPI_finish(); PG_RETURN_XML_P(cstring_to_xmltype(xmlschema));}Datumtable_to_xml_and_xmlschema(PG_FUNCTION_ARGS){ Oid relid = PG_GETARG_OID(0); bool nulls = PG_GETARG_BOOL(1); bool tableforest = PG_GETARG_BOOL(2); const char *targetns = _textout(PG_GETARG_TEXT_P(3)); Relation rel; const char *xmlschema; rel = heap_open(relid, AccessShareLock); xmlschema = map_sql_table_to_xmlschema(rel->rd_att, relid, nulls, tableforest, targetns); heap_close(rel, NoLock); PG_RETURN_XML_P(stringinfo_to_xmltype(table_to_xml_internal(relid, xmlschema, nulls, tableforest, targetns, true)));}Datumquery_to_xml_and_xmlschema(PG_FUNCTION_ARGS){ char *query = _textout(PG_GETARG_TEXT_P(0)); bool nulls = PG_GETARG_BOOL(1); bool tableforest = PG_GETARG_BOOL(2); const char *targetns = _textout(PG_GETARG_TEXT_P(3)); const char *xmlschema; SPIPlanPtr plan; Portal portal; SPI_connect(); if ((plan = SPI_prepare(query, 0, NULL)) == NULL) elog(ERROR, "SPI_prepare(\"%s\") failed", query); if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL) elog(ERROR, "SPI_cursor_open(\"%s\") failed", query);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -