/** * @file to_prov.cpp * @brief SQL function @c provsql.to_provxml() – XML/JSON circuit export. * * Implements @c provsql.to_provxml(), which serialises the provenance * circuit rooted at a given UUID token to an XML string. The XML * representation can be used for export, interchange, or display. * * The implementation retrieves the circuit from the mmap store via SPI, * traverses it depth-first, and builds the XML document using string * operations. Special XML characters in node labels are escaped by the * local @c xmlEscape() helper. */ extern "C" { #include "postgres.h" #include "fmgr.h" #include "catalog/pg_type.h" #include "utils/uuid.h" #include "provsql_shmem.h" #include "provsql_utils.h" PG_FUNCTION_INFO_V1(to_provxml); } #include #include #include #include #include #include #include "provenance_evaluate_compiled.hpp" using namespace std; /** * @brief Escape special XML characters in @p input. * @param input String to escape. * @return Escaped string suitable for embedding in XML. */ static string xmlEscape(const std::string& input) { string output; output.reserve(input.size()); for (char c : input) { switch (c) { case '&': output += "&"; break; case '<': output += "<"; break; default: output += c; break; } } return output; } /* From provenance_evaluate_compiled.cpp */ extern const char *drop_temp_table; bool join_with_temp_uuids(Oid table, const std::vector &uuids); /** * @brief Build an XML provenance representation for a circuit token. * @param tokenDatum Datum containing the root provenance gate UUID. * @param table Datum with the OID of the provenance mapping table. * @return XML string describing the provenance circuit. */ static string to_provxml_internal(Datum tokenDatum, Datum table) { pg_uuid_t token = *DatumGetUUIDP(tokenDatum); stringstream ss; ss << "\n"; ss << "\n"; GenericCircuit c = getGenericCircuit(token); auto root = c.getGate(uuid2string(token)); std::unordered_map provenance_mapping; if(table) { auto inputs = c.getInputs(); std::vector inputs_uuid; std::transform(inputs.begin(), inputs.end(), std::back_inserter(inputs_uuid), [&c](auto x) { return c.getUUID(x); }); bool drop_table = join_with_temp_uuids(table, inputs_uuid); constants_t constants = get_constants(true); initialize_provenance_mapping(constants, c, provenance_mapping, [](const char *v) { return std::string(v); }, drop_table); } std::list to_process { root }; std::set seen; while(!to_process.empty()) { auto g = to_process.front(); to_process.pop_front(); auto uuid = c.getUUID(g); gate_type type = c.getGateType(g); ss << " \n"; ss << " provsql:" + std::string(gate_type_name[type]) + "\n"; if (type == gate_input && table && provenance_mapping.find(g)!=provenance_mapping.end()) { ss << " " + xmlEscape(provenance_mapping[g]) + "\n"; } ss << " \n"; bool first=true; for(auto h: c.getWires(g)) { ss << " \n"; ss << " \n"; ss << " \n"; if(type == gate_monus) { if(first) ss << " left\n"; else ss << " right\n"; } ss << " \n"; if(seen.find(h) == seen.end()) { to_process.push_back(h); seen.insert(h); } first=false; } } ss << "\n"; return ss.str(); } /** @brief PostgreSQL-callable wrapper for to_provxml(). */ Datum to_provxml(PG_FUNCTION_ARGS) { try { Datum token = PG_GETARG_DATUM(0); Datum table = PG_GETARG_DATUM(1); std::string s = to_provxml_internal(token, table); text *result = (text *) palloc(VARHDRSZ + s.size() + 1); SET_VARSIZE(result, VARHDRSZ + s.size()); memcpy((void *) VARDATA(result), s.c_str(), s.size()); PG_RETURN_TEXT_P(result); } catch(const exception &e) { provsql_error("to_provxml: %s", e.what()); } catch (...) { provsql_error("to_provxml: Unknown exception"); } PG_RETURN_NULL(); }