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 <csignal>
29#include <utility>
30#include <sstream>
31#include <algorithm>
32
33using namespace std;
34
35/**
36 * @brief Parse a PostgreSQL text representation of an array of integer pairs.
37 * @param s String in the form @c {{a,b},{c,d},...}.
38 * @return Vector of (first, second) integer pairs.
39 */
40static vector<pair<int, int> > parse_array(string s)
41{
42 s = s.substr(1, s.size() - 2); // Remove initial '{' and final '}'
43
44 vector<pair<int, int> > result;
45
46 istringstream iss(s);
47 string p;
48
49 while(getline(iss, p, '}'))
50 {
51 if(!p.empty() && p[0]==',')
52 p=p.substr(1);
53
54 int k = p.find(",", 1);
55 string s1 = p.substr(1, k - 1);
56 int i1;
57 if (s1 == "NULL" || s1 == "")
58 i1 = 0;
59 else
60 i1 = stoi(p.substr(1, k - 1));
61 int i2 = stoi(p.substr(k + 1, p.size() - k));
62 result.push_back(make_pair(i1, i2));
63 }
64
65 return result;
66}
67
68/**
69 * @brief Build a GraphViz DOT representation of the provenance circuit at @p token.
70 * @param token Datum containing the root provenance gate UUID.
71 * @param token2prob Datum with the OID of the probability mapping relation.
72 * @param is_debug Datum boolean; if true, include debug information.
73 * @return DOT string for the circuit.
74 */
75static std::string view_circuit_internal(Datum token, Datum token2prob, Datum is_debug)
76{
77 Datum arguments[2]={token,token2prob};
78 constants_t constants=get_constants(true);
79 Oid argtypes[2]={constants.OID_TYPE_UUID,REGCLASSOID};
80 char nulls[2] = {' ',' '};
81
82 SPI_connect();
83
84 DotCircuit c;
85
86 int proc = 0;
87
88 if (SPI_execute_with_args(
89 "SELECT * FROM provsql.sub_circuit_with_desc($1,$2)", 2, argtypes, arguments, nulls, true, 0) == SPI_OK_SELECT)
90 {
91 proc = SPI_processed;
92 TupleDesc tupdesc = SPI_tuptable->tupdesc;
93 SPITupleTable *tuptable = SPI_tuptable;
94
95 for (int i = 0; i < proc; i++)
96 {
97 HeapTuple tuple = tuptable->vals[i];
98
99 string f = SPI_getvalue(tuple, tupdesc, 1);
100 string type = SPI_getvalue(tuple, tupdesc, 3);
101 if (type == "input")
102 {
103 c.setGate(f, DotGate::IN, SPI_getvalue(tuple, tupdesc, 4));
104
105 } else {
106 auto id=c.getGate(f);
107
108 if (type == "times")
109 {
111 }
112 else if (type == "plus")
113 {
115 }
116 else if (type == "monus")
117 {
119 }
120 else if (type == "monusr")
121 {
123 }
124 else if (type == "monusl")
125 {
127 }
128 else if (type == "delta")
129 {
131 }
132 else if (type == "eq")
133 {
134 vector<pair<int, int> > v = parse_array(std::string("{")+SPI_getvalue(tuple, tupdesc, 5)+"}");
135 if (v.size() != 1)
136 provsql_error("Incorrect extra information on eq gate");
137 std::string cond = std::to_string(v[0].first) + std::string("=") + std::to_string(v[0].second);
138 c.setGate(f, DotGate::EQ, cond);
139 }
140 else if (type == "project")
141 {
142 vector<pair<int, int> > v = parse_array(SPI_getvalue(tuple, tupdesc, 6));
143 sort(v.begin(), v.end(), [](auto &left, auto &right) {
144 return left.second < right.second;
145 });
146 std::string cond("(");
147 for (auto p : v)
148 {
149 cond += std::to_string(p.first) + ",";
150 }
151 std::string cond_final(cond.substr(0, cond.size() - 1) + ")");
152 //elog(WARNING, "PROJECT cond: %s", cond_final.c_str());
153 c.setGate(f, DotGate::PROJECT, cond_final);
154 }
155 else
156 {
157 provsql_error("Wrong type of gate in circuit");
158 }
159 //elog(WARNING, "%d -- %d", id, c.getGate(SPI_getvalue(tuple, tupdesc, 2)));
160 c.addWire(id, c.getGate(SPI_getvalue(tuple, tupdesc, 2)));
161 }
162 }
163 }
164
165 SPI_finish();
166
167 // Display the circuit for debugging:
168 int display = DatumGetInt64(is_debug);
169 if(display)
170 provsql_warning("%s", c.toString(gate_t{0}).c_str());
171
172 //Calling the dot renderer
173 return c.render();
174}
175
176/** @brief PostgreSQL-callable wrapper for view_circuit(). */
177Datum view_circuit(PG_FUNCTION_ARGS)
178{
179 try
180 {
181 Datum token = PG_GETARG_DATUM(0);
182 Datum token2prob = PG_GETARG_DATUM(1);
183 Datum is_debug = PG_GETARG_DATUM(2);
184
185 if (PG_ARGISNULL(1))
186 PG_RETURN_NULL();
187
188 std::string s = view_circuit_internal(token, token2prob, is_debug);
189
190 text *result = (text *) palloc(VARHDRSZ + s.size() + 1);
191 SET_VARSIZE(result, VARHDRSZ + s.size());
192
193 memcpy((void *) VARDATA(result),
194 s.c_str(),
195 s.size());
196 PG_RETURN_TEXT_P(result);
197 } catch(const std::exception &e) {
198 provsql_error("view_circuit: %s", e.what());
199 }
200 catch (...)
201 {
202 provsql_error("view_circuit: Unknown exception");
203 }
204
205 PG_RETURN_NULL();
206}
gate_t
Strongly-typed gate identifier.
Definition Circuit.h:48
Provenance circuit variant that renders to GraphViz DOT format.
@ PROJECT
Projection gate.
@ OPLUS
Semiring plus (⊕)
@ EQ
Equijoin gate.
@ OMINUS
Semiring monus (⊖), full.
@ OMINUSL
Monus, left child only.
@ OMINUSR
Monus, right child only.
@ DELTA
δ-semiring operator
@ IN
Input (variable) gate.
@ OTIMES
Semiring times (⊗)
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.
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.