ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
view_circuit.cpp
Go to the documentation of this file.
1/**
2 * @file view_circuit.cpp
3 * @brief SQL function @c provsql.view_circuit() – GraphViz DOT circuit visualisation.
4 *
5 * Implements @c provsql.view_circuit(), which serialises the provenance
6 * circuit rooted at a given UUID token to a GraphViz DOT string using the
7 * @c DotCircuit class. The returned string can be rendered to an image
8 * with @c dot(1) or @c graph-easy(1).
9 *
10 * An SPI query retrieves the optional provenance-mapping labels (human-
11 * readable identifiers for input gates) and attaches them to the DOT nodes
12 * before calling @c DotCircuit::render().
13 */
14extern "C"
15{
16#include "postgres.h"
17#include "fmgr.h"
18#include "catalog/pg_type.h"
19#include "utils/uuid.h"
20#include "executor/spi.h"
21#include "provsql_shmem.h"
22#include "provsql_utils.h"
23
24PG_FUNCTION_INFO_V1(view_circuit);
25}
26
27#include "DotCircuit.h"
28#include "tool_registry_sync.h"
29#include <csignal>
30#include <utility>
31#include <sstream>
32#include <algorithm>
33
34using namespace std;
35
36/**
37 * @brief Parse a PostgreSQL text representation of an array of integer pairs.
38 * @param s String in the form @c {{a,b},{c,d},...}.
39 * @return Vector of (first, second) integer pairs.
40 */
41static vector<pair<int, int> > parse_array(string s)
42{
43 s = s.substr(1, s.size() - 2); // Remove initial '{' and final '}'
44
45 vector<pair<int, int> > result;
46
47 istringstream iss(s);
48 string p;
49
50 while(getline(iss, p, '}'))
51 {
52 if(!p.empty() && p[0]==',')
53 p=p.substr(1);
54
55 int k = p.find(",", 1);
56 string s1 = p.substr(1, k - 1);
57 int i1;
58 if (s1 == "NULL" || s1 == "")
59 i1 = 0;
60 else
61 i1 = stoi(p.substr(1, k - 1));
62 int i2 = stoi(p.substr(k + 1, p.size() - k));
63 result.push_back(make_pair(i1, i2));
64 }
65
66 return result;
67}
68
69/**
70 * @brief Build a GraphViz DOT representation of the provenance circuit at @p token.
71 * @param token Datum containing the root provenance gate UUID.
72 * @param token2prob Datum with the OID of the probability mapping relation.
73 * @param is_debug Datum boolean; if true, include debug information.
74 * @return DOT string for the circuit.
75 */
76static std::string view_circuit_internal(Datum token, Datum token2prob, Datum is_debug)
77{
78 Datum arguments[2]={token,token2prob};
79 constants_t constants=get_constants(true);
80 Oid argtypes[2]={constants.OID_TYPE_UUID,REGCLASSOID};
81 char nulls[2] = {' ',' '};
82
83 SPI_connect();
84
85 DotCircuit c;
86
87 int proc = 0;
88
89 if (SPI_execute_with_args(
90 "SELECT * FROM provsql.sub_circuit_with_desc($1,$2)", 2, argtypes, arguments, nulls, true, 0) == SPI_OK_SELECT)
91 {
92 proc = SPI_processed;
93 TupleDesc tupdesc = SPI_tuptable->tupdesc;
94 SPITupleTable *tuptable = SPI_tuptable;
95
96 for (int i = 0; i < proc; i++)
97 {
98 HeapTuple tuple = tuptable->vals[i];
99
100 string f = SPI_getvalue(tuple, tupdesc, 1);
101 string type = SPI_getvalue(tuple, tupdesc, 3);
102 if (type == "input")
103 {
104 c.setGate(f, DotGate::IN, SPI_getvalue(tuple, tupdesc, 4));
105
106 } else {
107 auto id=c.getGate(f);
108
109 if (type == "times" || type == "assumed_boolean" || type == "annotation")
110 {
111 /* assumed_boolean and annotation are single-child structural
112 * wrappers. A single-wire TIMES renders as identity in the DOT
113 * output, which is what we want here; rendering a distinct node
114 * would clutter the graph for no semantic gain. PROV-XML export
115 * keeps the wrapper explicit. */
117 }
118 else if (type == "plus")
119 {
121 }
122 else if (type == "monus")
123 {
125 }
126 else if (type == "monusr")
127 {
129 }
130 else if (type == "monusl")
131 {
133 }
134 else if (type == "delta")
135 {
137 }
138 else if (type == "eq")
139 {
140 vector<pair<int, int> > v = parse_array(std::string("{")+SPI_getvalue(tuple, tupdesc, 5)+"}");
141 if (v.size() != 1)
142 provsql_error("Incorrect extra information on eq gate");
143 std::string cond = std::to_string(v[0].first) + std::string("=") + std::to_string(v[0].second);
144 c.setGate(f, DotGate::EQ, cond);
145 }
146 else if (type == "project")
147 {
148 vector<pair<int, int> > v = parse_array(SPI_getvalue(tuple, tupdesc, 6));
149 sort(v.begin(), v.end(), [](auto &left, auto &right) {
150 return left.second < right.second;
151 });
152 std::string cond("(");
153 for (auto p : v)
154 {
155 cond += std::to_string(p.first) + ",";
156 }
157 std::string cond_final(cond.substr(0, cond.size() - 1) + ")");
158 //elog(WARNING, "PROJECT cond: %s", cond_final.c_str());
159 c.setGate(f, DotGate::PROJECT, cond_final);
160 }
161 else
162 {
163 provsql_error("Unknown type of gate: %s", type.c_str());
164 }
165 //elog(WARNING, "%d -- %d", id, c.getGate(SPI_getvalue(tuple, tupdesc, 2)));
166 c.addWire(id, c.getGate(SPI_getvalue(tuple, tupdesc, 2)));
167 }
168 }
169 }
170
171 SPI_finish();
172
173 // Display the circuit for debugging:
174 int display = DatumGetInt64(is_debug);
175 if(display)
176 provsql_warning("%s", c.toString(gate_t{0}).c_str());
177
178 //Calling the dot renderer
179 return c.render();
180}
181
182/** @brief PostgreSQL-callable wrapper for view_circuit(). */
183Datum view_circuit(PG_FUNCTION_ARGS)
184{
185 provsql_sync_tool_registry(); // honour persisted tool-registry overrides
186 try
187 {
188 Datum token = PG_GETARG_DATUM(0);
189 Datum token2prob = PG_GETARG_DATUM(1);
190 Datum is_debug = PG_GETARG_DATUM(2);
191
192 if (PG_ARGISNULL(1))
193 PG_RETURN_NULL();
194
195 std::string s = view_circuit_internal(token, token2prob, is_debug);
196
197 text *result = (text *) palloc(VARHDRSZ + s.size() + 1);
198 SET_VARSIZE(result, VARHDRSZ + s.size());
199
200 memcpy((void *) VARDATA(result),
201 s.c_str(),
202 s.size());
203 PG_RETURN_TEXT_P(result);
204 } catch(const std::exception &e) {
205 provsql_error("view_circuit: %s", e.what());
206 }
207 catch (...)
208 {
209 provsql_error("view_circuit: Unknown exception");
210 }
211
212 PG_RETURN_NULL();
213}
gate_t
Strongly-typed gate identifier.
Definition Circuit.h:49
Provenance circuit variant that renders to GraphViz DOT format.
@ PROJECT
Projection gate.
Definition DotCircuit.h:49
@ OPLUS
Semiring plus (⊕).
Definition DotCircuit.h:45
@ EQ
Equijoin gate.
Definition DotCircuit.h:50
@ OMINUS
Semiring monus (⊖), full.
Definition DotCircuit.h:46
@ OMINUSL
Monus, left child only.
Definition DotCircuit.h:48
@ OMINUSR
Monus, right child only.
Definition DotCircuit.h:47
@ DELTA
δ-semiring operator
Definition DotCircuit.h:52
@ IN
Input (variable) gate.
Definition DotCircuit.h:51
@ OTIMES
Semiring times (⊗).
Definition DotCircuit.h:44
void addWire(gate_t f, gate_t t)
Add a directed wire from gate f (parent) to gate t (child).
Definition Circuit.hpp:81
gate_t getGate(const uuid &u)
Return (or create) the gate associated with UUID u.
Definition Circuit.hpp:33
Circuit specialisation for GraphViz DOT rendering.
Definition DotCircuit.h:61
virtual std::string toString(gate_t g) const override
Return a textual description of gate g for debugging.
gate_t setGate(const uuid &u, DotGate type) override
Create or update the gate associated with UUID u.
std::string render() const
Render the entire circuit as a GraphViz DOT digraph string.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
#define provsql_warning(fmt,...)
Emit a ProvSQL warning message (execution continues).
Shared-memory segment and inter-process pipe management.
constants_t get_constants(bool failure_if_not_possible)
Retrieve the cached OID constants for the current database.
Core types, constants, and utilities shared across ProvSQL.
Structure to store the value of various constants.
Oid OID_TYPE_UUID
OID of the uuid TYPE.
void provsql_sync_tool_registry()
Rebuild the in-memory registry as "compiled seed overlaid with the provsql.tool_overrides rows"...
Reload the in-memory external-tool registry from its persistent overrides.
static std::string view_circuit_internal(Datum token, Datum token2prob, Datum is_debug)
Build a GraphViz DOT representation of the provenance circuit at token.
Datum view_circuit(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for view_circuit().
static vector< pair< int, int > > parse_array(string s)
Parse a PostgreSQL text representation of an array of integer pairs.