ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
MMappedCircuit.cpp
Go to the documentation of this file.
1/**
2 * @file MMappedCircuit.cpp
3 * @brief Persistent mmap-backed circuit: implementation and background-worker entry points.
4 *
5 * Implements the @c MMappedCircuit methods declared in @c MMappedCircuit.h,
6 * the @c createGenericCircuit() free function, and the background-worker
7 * entry points declared in @c provsql_mmap.h:
8 *
9 * - @c initialize_provsql_mmap(): called by the background worker at
10 * startup; opens all four mmap files and creates the singleton
11 * @c MMappedCircuit instance.
12 * - @c destroy_provsql_mmap(): called on shutdown; syncs and deletes the
13 * singleton.
14 * - @c provsql_mmap_main_loop(): the worker's main loop; receives gate-
15 * creation messages from backends over the IPC pipe and writes them
16 * to the mmap store.
17 *
18 * The @c createGenericCircuit() function performs a BFS from a root UUID,
19 * reading gates from the mmap store and building an in-memory @c GenericCircuit.
20 */
21#include <cerrno>
22#include <cmath>
23#include <map>
24#include <sstream>
25#include <string>
26
27#include "MMappedCircuit.h"
28#include "GenericCircuit.h"
29#include "Circuit.hpp"
30#include "provsql_utils_cpp.h"
31
32extern "C" {
33#include "miscadmin.h"
34#include "provsql_mmap.h"
35#include "provsql_shmem.h"
36}
37
38/** @brief Per-database mmap-backed provenance circuits, keyed by database OID. */
39static std::map<Oid, MMappedCircuit*> circuits;
40
41std::string MMappedCircuit::makePath(Oid db_oid, const char *filename)
42{
43 return std::string(DataDir) + "/base/" + std::to_string(db_oid) + "/" + filename;
44}
45
46MMappedCircuit::MMappedCircuit(Oid db_oid, bool read_only) :
49 makePath(db_oid, GATES_FILENAME),
50 makePath(db_oid, WIRES_FILENAME),
51 makePath(db_oid, EXTRA_FILENAME),
53 read_only) {}
54
56{
57 /* circuits are opened lazily on first IPC message */
58}
59
61{
62 for(auto &kv: circuits)
63 delete kv.second;
64 circuits.clear();
65}
66
68 pg_uuid_t token, gate_type type, const std::vector<pg_uuid_t> &children)
69{
70 auto [idx, created] = mapping.add(token);
71 if(!created) {
72 // The gate may have been lazy-added as a default gate_input below
73 // (when an earlier-arriving parent createGate referenced it as a
74 // child whose own createGate had not yet been received). Under
75 // concurrent backends, parent/child IPCs from different sessions
76 // can be interleaved such that the parent's lazy-add wins and the
77 // real create for the child is then silently dropped. Detect that
78 // case and upgrade the placeholder in place; otherwise leave the
79 // existing gate alone (real duplicate creation, idempotent).
80 bool placeholder = gates[idx].type == gate_input
81 && gates[idx].nb_children == 0;
82 bool real_create = type != gate_input || !children.empty();
83 if(placeholder && real_create) {
84 gates[idx].type = type;
85 gates[idx].nb_children = static_cast<unsigned>(children.size());
86 gates[idx].children_idx = wires.nbElements();
87 for(const auto &c: children)
88 wires.add(c);
89 for(const auto &c: children) {
90 auto [child_idx, child_created] = mapping.add(c);
91 if(child_created)
92 gates.add({gate_input, 0, wires.nbElements()});
93 }
94 }
95 return;
96 }
97
98 gates.add({type, static_cast<unsigned>(children.size()), wires.nbElements()});
99 for(const auto &c: children)
100 wires.add(c);
101
102 for(const auto &c: children) {
103 auto [child_idx, child_created] = mapping.add(c);
104 if(child_created)
105 gates.add({gate_input, 0, wires.nbElements()});
106 }
107}
108
110{
111 auto idx = mapping[token];
113 return gate_input;
114 else
115 return gates[idx].type;
116}
117
118std::vector<pg_uuid_t> MMappedCircuit::getChildren(pg_uuid_t token) const
119{
120 std::vector<pg_uuid_t> result;
121 auto idx = mapping[token];
123 const GateInformation &gi = gates[idx];
124 for(unsigned long k=gi.children_idx; k<gi.children_idx+gi.nb_children; ++k)
125 result.push_back(wires[k]);
126 }
127 return result;
128}
129
130bool MMappedCircuit::setProb(pg_uuid_t token, double prob)
131{
132 auto [idx, created] = mapping.add(token);
133 if(created)
134 gates.add({gate_input, 0, wires.nbElements()});
135 if(gates[idx].type == gate_input || gates[idx].type == gate_update || gates[idx].type == gate_mulinput) {
136 gates[idx].prob = prob;
137 return true;
138 }
139 return false;
140}
141
143{
144 auto idx = mapping[token];
146 (gates[idx].type != gate_input && gates[idx].type != gate_update && gates[idx].type != gate_mulinput))
147 return NAN;
148 else
149 return gates[idx].prob;
150}
151
152void MMappedCircuit::setInfos(pg_uuid_t token, unsigned info1, unsigned info2)
153{
154 auto idx = mapping[token];
156 gates[idx].info1=info1;
157 gates[idx].info2=info2;
158 }
159}
160
161void MMappedCircuit::setExtra(pg_uuid_t token, const std::string &s)
162{
163 auto idx = mapping[token];
165 gates[idx].extra_idx=extra.nbElements();
166 for(auto c: s)
167 extra.add(c);
168 gates[idx].extra_len=s.size();
169 }
170}
171
172std::pair<unsigned, unsigned> MMappedCircuit::getInfos(pg_uuid_t token) const
173{
174 auto idx = mapping[token];
176 return std::make_pair(0, 0);
177 } else {
178 const GateInformation &gi = gates[idx];
179 return std::make_pair(gi.info1, gi.info2);
180 }
181}
182
183std::string MMappedCircuit::getExtra(pg_uuid_t token) const
184{
185 std::string result;
186
187 auto idx = mapping[token];
189 for(unsigned long start=gates[idx].extra_idx, k=start, end=start+gates[idx].extra_len; k<end; ++k)
190 result+=extra[k];
191 }
192
193 return result;
194}
195
196/** @brief Return (creating lazily if needed) the circuit for @p db_oid. */
197static MMappedCircuit *getCircuit(Oid db_oid)
198{
199 auto it = circuits.find(db_oid);
200 if(it == circuits.end()) {
201 circuits[db_oid] = new MMappedCircuit(db_oid);
202 return circuits[db_oid];
203 }
204 return it->second;
205}
206
207#ifdef PROVSQL_INPROCESS_STORE
208/* Single-process build: the backend builds the in-memory circuit directly
209 from this process's store, instead of round-tripping a Boost-serialised
210 copy through the FIFO (which only existed to cross the worker/backend
211 process boundary). This is also what lets the WASM build avoid the
212 compiled libboost_serialization dependency. */
213GenericCircuit provsql_inproc_generic_circuit(pg_uuid_t token)
214{
215 return getCircuit(MyDatabaseId)->createGenericCircuit(token);
216}
217
218GenericCircuit provsql_inproc_joint_circuit(pg_uuid_t root, pg_uuid_t event)
219{
220 return getCircuit(MyDatabaseId)->createGenericCircuit(
221 std::vector<pg_uuid_t>{root, event});
222}
223#endif
224
225extern "C" void provsql_mmap_dispatch(char c, Oid db_oid)
226{
227 MMappedCircuit *circuit = getCircuit(db_oid);
228
229 switch(c) {
230 case 'C':
231 {
232 pg_uuid_t token;
233 gate_type type;
234 unsigned nb_children;
235
236 if(!READM(token, pg_uuid_t) || !READM(type, gate_type) || !READM(nb_children, unsigned))
237 provsql_error("Cannot read from pipe (message type C)"); ;
238
239 std::vector<pg_uuid_t> children(nb_children);
240 for(unsigned i=0; i<nb_children; ++i)
241 if(!READM(children[i], pg_uuid_t))
242 provsql_error("Cannot read from pipe (message type C)");
243
244 circuit->createGate(token, type, children);
245 break;
246 }
247
248 case 'P':
249 {
250 pg_uuid_t token;
251 double prob;
252
253 if(!READM(token, pg_uuid_t) || !READM(prob, double))
254 provsql_error("Cannot read from pipe (message type P)");
255
256 bool ok = circuit->setProb(token, prob);
257 char return_value = ok?static_cast<char>(1):0;
258
259 if(!WRITEB(&return_value, char))
260 provsql_error("Cannot write response to pipe (message type P)");
261 break;
262 }
263
264 case 'I':
265 {
266 pg_uuid_t token;
267 unsigned info1, info2;
268
269 if(!READM(token, pg_uuid_t) || !READM(info1, unsigned) || !READM(info2, unsigned))
270 provsql_error("Cannot read from pipe (message type I)");
271
272 circuit->setInfos(token, info1, info2);
273 break;
274 }
275
276 case 'E':
277 {
278 pg_uuid_t token;
279 unsigned len;
280
281 if(!READM(token, pg_uuid_t) || !READM(len, unsigned))
282 provsql_error("Cannot read from pipe (message type E)");
283
284 if(len>0) {
285 char *data = new char[len];
286 if(!READM_BYTES(data, len))
287 provsql_error("Cannot read from pipe (message type E)");
288
289 circuit->setExtra(token, std::string(data, len));
290 }
291
292 break;
293 }
294
295 case 't':
296 {
297 pg_uuid_t token;
298
299 if(!READM(token, pg_uuid_t))
300 provsql_error("Cannot read from pipe (message type t)");
301
302 gate_type type = circuit->getGateType(token);
303
304 if(!WRITEB(&type, gate_type))
305 provsql_error("Cannot write response to pipe (message type t)");
306 break;
307 }
308
309 case 'n':
310 {
311 unsigned long nb = circuit->getNbGates();
312
313 if(!WRITEB(&nb, unsigned long))
314 provsql_error("Cannot write response to pipe (message type n)");
315 break;
316 }
317
318 case 'c':
319 {
320 pg_uuid_t token;
321
322 if(!READM(token, pg_uuid_t))
323 provsql_error("Cannot read from pipe (message type c)");
324
325 auto children = circuit->getChildren(token);
326 unsigned nb_children = children.size();
327 if(!WRITEB(&nb_children, unsigned))
328 provsql_error("Cannot write response to pipe (message type c)");
329
330 if(!WRITEB_BYTES(children.data(), nb_children*sizeof(pg_uuid_t)))
331 provsql_error("Cannot write response to pipe (message type c)");
332 break;
333 }
334
335 case 'p':
336 {
337 pg_uuid_t token;
338
339 if(!READM(token, pg_uuid_t))
340 provsql_error("Cannot read from pipe (message type p)");
341
342 double prob = circuit->getProb(token);
343
344 if(!WRITEB(&prob, double))
345 provsql_error("Cannot write response to pipe (message type p)");
346 break;
347 }
348
349 case 'i':
350 {
351 pg_uuid_t token;
352
353 if(!READM(token, pg_uuid_t))
354 provsql_error("Cannot read from pipe (message type i)");
355
356 auto infos = circuit->getInfos(token);
357
358 if(!WRITEB(&infos.first, unsigned) || !WRITEB(&infos.second, unsigned))
359 provsql_error("Cannot write response to pipe (message type i)");
360 break;
361 }
362
363 case 'e':
364 {
365 pg_uuid_t token;
366
367 if(!READM(token, pg_uuid_t))
368 provsql_error("Cannot read from pipe (message type e)");
369
370 auto str = circuit->getExtra(token);
371 unsigned len = str.size();
372
373 if(!WRITEB(&len, unsigned) || !WRITEB_BYTES(str.data(), len))
374 provsql_error("Cannot write response to pipe (message type e)");
375 break;
376 }
377
378 case 'g':
379 {
380 pg_uuid_t token;
381
382 if(!READM(token, pg_uuid_t))
383 provsql_error("Cannot read from pipe (message type g)");
384
385#ifdef PROVSQL_INPROCESS_STORE
386 /* Unreachable: the backend calls provsql_inproc_generic_circuit
387 directly instead of issuing the 'g' message. */
388 provsql_error("message type g is not used by the in-process store");
389#else
390 std::stringstream ss;
391 boost::archive::binary_oarchive oa(ss);
392 oa << circuit->createGenericCircuit(token);
393
394 ss.seekg(0, std::ios::end);
395 unsigned long size = ss.tellg();
396 ss.seekg(0, std::ios::beg);
397
398 if(!WRITEB(&size, unsigned long) || !WRITEB_BYTES(ss.str().data(), size))
399 provsql_error("Cannot write to pipe (message type g)");
400#endif
401 break;
402 }
403
404 case 'T':
405 {
406 /* Insert / upsert per-table provenance metadata. */
407 ProvenanceTableInfo info{};
408 if(!READM(info.relid, Oid) || !READM(info.kind, uint8_t)
409 || !READM(info.block_key_n, uint16_t))
410 provsql_error("Cannot read from pipe (message type T)");
412 provsql_error("ProvSQL: block key wider than %d columns "
413 "(message type T)", PROVSQL_TABLE_INFO_MAX_BLOCK_KEY);
414 for(uint16_t i=0; i<info.block_key_n; ++i)
415 if(!READM(info.block_key[i], AttrNumber))
416 provsql_error("Cannot read from pipe (message type T)");
417 circuit->setTableInfo(info);
418 break;
419 }
420
421 case 'D':
422 {
423 /* Delete per-table provenance metadata. */
424 Oid relid;
425 if(!READM(relid, Oid))
426 provsql_error("Cannot read from pipe (message type D)");
427 circuit->removeTableInfo(relid);
428 break;
429 }
430
431 case 's':
432 {
433 /* Look up per-table provenance metadata. */
434 Oid relid;
435 if(!READM(relid, Oid))
436 provsql_error("Cannot read from pipe (message type s)");
437 ProvenanceTableInfo info{};
438 char found = circuit->getTableInfo(relid, info) ? 1 : 0;
439 if(!WRITEB(&found, char))
440 provsql_error("Cannot write response to pipe (message type s)");
441 if(found) {
442 if(!WRITEB(&info.kind, uint8_t) || !WRITEB(&info.block_key_n, uint16_t))
443 provsql_error("Cannot write response to pipe (message type s)");
444 for(uint16_t i=0; i<info.block_key_n; ++i)
445 if(!WRITEB(&info.block_key[i], AttrNumber))
446 provsql_error("Cannot write response to pipe (message type s)");
447 }
448 break;
449 }
450
451 case 'A':
452 {
453 /* Insert / upsert the ancestor half of a per-table metadata
454 * record (the kind / block_key half is preserved). */
455 Oid relid;
456 uint16_t ancestor_n;
457 if(!READM(relid, Oid) || !READM(ancestor_n, uint16_t))
458 provsql_error("Cannot read from pipe (message type A)");
459 if(ancestor_n > PROVSQL_TABLE_INFO_MAX_ANCESTORS)
460 provsql_error("ProvSQL: ancestor set wider than %d entries "
461 "(message type A)",
464 for(uint16_t i=0; i<ancestor_n; ++i)
465 if(!READM(ancestors[i], Oid))
466 provsql_error("Cannot read from pipe (message type A)");
467 circuit->setTableAncestry(relid, ancestor_n, ancestors);
468 break;
469 }
470
471 case 'R':
472 {
473 /* Clear just the ancestor half of a per-table metadata record. */
474 Oid relid;
475 if(!READM(relid, Oid))
476 provsql_error("Cannot read from pipe (message type R)");
477 circuit->removeTableAncestry(relid);
478 break;
479 }
480
481 case 'a':
482 {
483 /* Look up just the ancestor half of a per-table metadata record. */
484 Oid relid;
485 if(!READM(relid, Oid))
486 provsql_error("Cannot read from pipe (message type a)");
487 ProvenanceTableInfo info{};
488 char found = circuit->getTableInfo(relid, info) ? 1 : 0;
489 if(!WRITEB(&found, char))
490 provsql_error("Cannot write response to pipe (message type a)");
491 if(found) {
492 if(!WRITEB(&info.ancestor_n, uint16_t))
493 provsql_error("Cannot write response to pipe (message type a)");
494 for(uint16_t i=0; i<info.ancestor_n; ++i)
495 if(!WRITEB(&info.ancestors[i], Oid))
496 provsql_error("Cannot write response to pipe (message type a)");
497 }
498 break;
499 }
500
501 case 'j':
502 {
503 /* Joint-circuit load: BFS from a vector of roots so a shared
504 * subgraph reachable from multiple roots collapses to a single
505 * gate_t. Used by getJointCircuit() to load an RV's sub-DAG
506 * together with a conditioning gate that sits above it in the
507 * persisted DAG. */
508 unsigned nb_roots;
509 if(!READM(nb_roots, unsigned))
510 provsql_error("Cannot read from pipe (message type j)");
511
512 std::vector<pg_uuid_t> roots(nb_roots);
513 for(unsigned i=0; i<nb_roots; ++i)
514 if(!READM(roots[i], pg_uuid_t))
515 provsql_error("Cannot read from pipe (message type j)");
516
517#ifdef PROVSQL_INPROCESS_STORE
518 /* Unreachable: the backend calls provsql_inproc_joint_circuit
519 directly instead of issuing the 'j' message. */
520 provsql_error("message type j is not used by the in-process store");
521#else
522 std::stringstream ss;
523 boost::archive::binary_oarchive oa(ss);
524 oa << circuit->createGenericCircuit(roots);
525
526 ss.seekg(0, std::ios::end);
527 unsigned long size = ss.tellg();
528 ss.seekg(0, std::ios::beg);
529
530 if(!WRITEB(&size, unsigned long) || !WRITEB_BYTES(ss.str().data(), size))
531 provsql_error("Cannot write to pipe (message type j)");
532#endif
533 break;
534 }
535
536 default:
537 provsql_error("Wrong message type: %c", c);
538 }
539}
540
541#ifndef PROVSQL_INPROCESS_STORE
543{
544 char c;
545
546 while(READM(c, char)) {
547 Oid db_oid;
548 if(!READM(db_oid, Oid))
549 provsql_error("Cannot read database OID from pipe");
550 provsql_mmap_dispatch(c, db_oid);
551 }
552
553 int e = errno;
554 provsql_error("Reading from pipe: %s", strerror(e));
555}
556#endif
557
559{
560 gates.sync();
561 wires.sync();
562 mapping.sync();
563 extra.sync();
564 tableInfo.sync();
565}
566
567/* The tableInfo vector uses a tombstone scheme: removed entries have
568 * their relid set to InvalidOid and remain in place. setTableInfo()
569 * reuses tombstone slots before appending. All readers skip
570 * InvalidOid entries. This avoids reaching into MMappedVector's
571 * append-only public API, and keeps the file format trivial: a
572 * crash-recovered file is internally consistent without any extra
573 * recovery step. In practice, churn on this vector is low (one
574 * entry per add_provenance / repair_key / remove_provenance call).
575 *
576 * Each record carries two logically independent halves: the kind /
577 * block_key fields (TID / BID classification, set by add_provenance /
578 * repair_key / set_table_info) and the ancestor_n / ancestors fields
579 * (base-relation provenance lineage, set by add_provenance for base
580 * tables and by the CTAS hook for derived tables). setTableInfo()
581 * updates the kind half and preserves the ancestor half on update;
582 * setTableAncestry() does the converse. This lets the two halves
583 * evolve independently without the SQL layer having to fetch-and-
584 * round-trip every time. */
585
587{
588 long tombstone = -1;
589 unsigned long n = tableInfo.nbElements();
590 for(unsigned long i=0; i<n; ++i) {
591 if(tableInfo[i].relid == info.relid) {
592 /* Preserve the existing ancestor half on update. */
593 ProvenanceTableInfo merged = info;
594 merged.ancestor_n = tableInfo[i].ancestor_n;
595 memcpy(merged.ancestors, tableInfo[i].ancestors,
596 merged.ancestor_n * sizeof(Oid));
597 tableInfo[i] = merged;
598 return;
599 }
600 if(tombstone < 0 && tableInfo[i].relid == InvalidOid)
601 tombstone = static_cast<long>(i);
602 }
603 /* Fresh record: kind half from caller, ancestor half empty. */
604 ProvenanceTableInfo fresh = info;
605 fresh.ancestor_n = 0;
606 if(tombstone >= 0)
607 tableInfo[tombstone] = fresh;
608 else
609 tableInfo.add(fresh);
610}
611
612void MMappedCircuit::setTableAncestry(Oid relid, uint16_t ancestor_n,
613 const Oid *ancestors)
614{
615 if(relid == InvalidOid)
616 return;
617 if(ancestor_n > PROVSQL_TABLE_INFO_MAX_ANCESTORS)
618 return; /* defensive: caller-side check already rejects this */
619 unsigned long n = tableInfo.nbElements();
620 for(unsigned long i=0; i<n; ++i) {
621 if(tableInfo[i].relid == relid) {
622 ProvenanceTableInfo updated = tableInfo[i];
623 updated.ancestor_n = ancestor_n;
624 memcpy(updated.ancestors, ancestors, ancestor_n * sizeof(Oid));
625 tableInfo[i] = updated;
626 return;
627 }
628 }
629 /* No-op when relid has no kind record: callers should set kind
630 * first (add_provenance / repair_key do this). Silently
631 * dropping the ancestry payload here matches the existing
632 * removeTableInfo / setTableInfo "missing relid is harmless"
633 * pattern and avoids creating an OPAQUE-by-default record. */
634}
635
637{
638 if(relid == InvalidOid)
639 return;
640 unsigned long n = tableInfo.nbElements();
641 for(unsigned long i=0; i<n; ++i) {
642 if(tableInfo[i].relid == relid) {
643 tableInfo[i].relid = InvalidOid;
644 return;
645 }
646 }
647}
648
650{
651 if(relid == InvalidOid)
652 return;
653 unsigned long n = tableInfo.nbElements();
654 for(unsigned long i=0; i<n; ++i) {
655 if(tableInfo[i].relid == relid) {
656 tableInfo[i].ancestor_n = 0;
657 return;
658 }
659 }
660}
661
663{
664 if(relid == InvalidOid)
665 return false;
666 for(unsigned long i=0; i<tableInfo.nbElements(); ++i) {
667 if(tableInfo[i].relid == relid) {
668 out = tableInfo[i];
669 return true;
670 }
671 }
672 return false;
673}
674
675/**
676 * @brief Lexicographic less-than comparison for @c pg_uuid_t.
677 * @param a Left UUID.
678 * @param b Right UUID.
679 * @return @c true if @p a is lexicographically less than @p b.
680 */
681bool operator<(const pg_uuid_t a, const pg_uuid_t b)
682{
683 return memcmp(&a, &b, sizeof(pg_uuid_t))<0;
684}
685
687{
688 return createGenericCircuit(std::vector<pg_uuid_t>{token});
689}
690
692 const std::vector<pg_uuid_t> &roots) const
693{
694 /* Seed the work list with every root. std::set deduplicates so a
695 * UUID listed twice (or reached as a child of one root and the
696 * other's root itself) is processed only once. Shared subgraphs
697 * therefore land on a single gate_t in `result` -- the property
698 * that lets the conditional MC sampler couple the indicator and
699 * value paths through @c Sampler::scalar_cache_ / @c bool_cache_. */
700 std::set<pg_uuid_t> to_process, processed;
701 for(const auto &r : roots)
702 to_process.insert(r);
703
704 GenericCircuit result;
705
706 while(!to_process.empty()) {
707 pg_uuid_t uuid = *to_process.begin();
708 to_process.erase(to_process.begin());
709 processed.insert(uuid);
710 std::string f{uuid2string(uuid)};
711
712 gate_type type = getGateType(uuid);
713 gate_t id = result.setGate(f, type);
714 double prob = getProb(uuid);
715 if(!std::isnan(prob))
716 result.setProb(id, prob);
717
718 std::vector<pg_uuid_t> children = getChildren(uuid);
719 for(unsigned i=0; i<children.size(); ++i) {
720 result.addWire(
721 id,
722 result.getGate(uuid2string(children[i])));
723 if(processed.find(children[i])==processed.end())
724 to_process.insert(children[i]);
725 }
726
727 if(type==gate_mulinput || type==gate_eq || type==gate_agg
728 || type==gate_cmp || type==gate_arith) {
729 auto [info1, info2] = getInfos(uuid);
730 result.setInfos(id, info1, info2);
731 } else if(type==gate_plus || type==gate_times) {
732 /* The d-DNNF certificate (DNNF_CERT_INFO in info1: deterministic
733 * plus / decomposable times). Copied only when set, so unmarked
734 * gates do not bloat the in-memory infos map with zeros. */
735 auto [info1, info2] = getInfos(uuid);
736 if(info1 != 0 || info2 != 0)
737 result.setInfos(id, info1, info2);
738 }
739
740 if(type==gate_project || type==gate_value || type==gate_agg
741 || type==gate_rv || type==gate_mulinput || type==gate_annotation
742 || type==gate_assumed || type==gate_mobius) {
743 /* gate_assumed carries its assumption kind ('boolean' /
744 * 'absorptive') in extra; gate_mobius carries its per-child integer
745 * coefficients ("uuid:coeff" tokens); gates stored without the
746 * label have none and default to 'boolean' at
747 * evaluation. */
748 auto extra = getExtra(uuid);
749 result.setExtra(id, extra);
750 }
751 }
752
753 return result;
754}
gate_t
Strongly-typed gate identifier.
Definition Circuit.h:49
Out-of-line template method implementations for Circuit<gateType>.
Semiring-agnostic in-memory provenance circuit.
void provsql_mmap_dispatch(char c, Oid db_oid)
Handle a single IPC message: read its payload and write its reply.
static std::map< Oid, MMappedCircuit * > circuits
Per-database mmap-backed provenance circuits, keyed by database OID.
void destroy_provsql_mmap()
Unmap and close the mmap files.
bool operator<(const pg_uuid_t a, const pg_uuid_t b)
Lexicographic less-than comparison for pg_uuid_t.
void provsql_mmap_main_loop()
Main processing loop of the mmap background worker.
void initialize_provsql_mmap()
Open (or create) the mmap files and initialise the circuit store.
static MMappedCircuit * getCircuit(Oid db_oid)
Return (creating lazily if needed) the circuit for db_oid.
Persistent, mmap-backed storage for the full provenance circuit.
#define PROVSQL_TABLE_INFO_MAX_BLOCK_KEY
Cap on the number of block-key columns recorded per relation.
#define PROVSQL_TABLE_INFO_MAX_ANCESTORS
Cap on the number of base ancestors recorded per relation.
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
In-memory provenance circuit with semiring-generic evaluation.
void setInfos(gate_t g, unsigned info1, unsigned info2)
Set the integer annotation pair for gate g.
gate_t setGate(gate_type type) override
Allocate a new gate with type type and no UUID.
void setExtra(gate_t g, const std::string &ex)
Attach a string extra to gate g.
void setProb(gate_t g, double p)
Set the probability for gate g.
Persistent mmap-backed representation of the provenance circuit.
void setTableAncestry(Oid relid, uint16_t ancestor_n, const Oid *ancestors)
Insert or update the ancestor set of a per-table metadata record, preserving any existing kind / bloc...
void setExtra(pg_uuid_t token, const std::string &s)
Attach a variable-length string annotation to a gate.
void setTableInfo(const ProvenanceTableInfo &info)
Insert or update the kind / block_key half of a per-table metadata record, preserving any existing an...
void removeTableInfo(Oid relid)
Remove a per-table metadata entry (both halves).
MMappedUUIDHashTable mapping
UUID → gate-index hash table.
void createGate(pg_uuid_t token, gate_type type, const std::vector< pg_uuid_t > &children)
Persist a new gate to the mmap store.
std::string getExtra(pg_uuid_t token) const
Return the variable-length string annotation for gate token.
unsigned long getNbGates() const
Return the total number of gates stored in the circuit.
static constexpr const char * GATES_FILENAME
Backing file for gates.
gate_type getGateType(pg_uuid_t token) const
Return the type of the gate identified by token.
static constexpr const char * TABLE_INFO_FILENAME
Backing file for tableInfo.
void removeTableAncestry(Oid relid)
Clear just the ancestor set of a per-table metadata record, preserving kind / block_key.
void sync()
Flush all backing files to disk with msync().
MMappedVector< ProvenanceTableInfo > tableInfo
Per-relation TID/BID metadata (safe-query optimisation).
static constexpr const char * WIRES_FILENAME
Backing file for wires.
GenericCircuit createGenericCircuit(pg_uuid_t token) const
Build an in-memory GenericCircuit rooted at token.
static std::string makePath(Oid db_oid, const char *filename)
Build the full path for a mmap file under $PGDATA/base/<db_oid>/.
bool setProb(pg_uuid_t token, double prob)
Set the probability associated with a gate.
static constexpr const char * EXTRA_FILENAME
Backing file for extra.
static constexpr const char * MAPPING_FILENAME
Backing file for mapping.
bool getTableInfo(Oid relid, ProvenanceTableInfo &out) const
Look up the full per-table metadata record (both halves).
MMappedVector< char > extra
Variable-length string data.
double getProb(pg_uuid_t token) const
Return the probability stored for the gate identified by token.
std::vector< pg_uuid_t > getChildren(pg_uuid_t token) const
Return the child UUIDs of the gate identified by token.
MMappedVector< GateInformation > gates
Gate metadata array.
MMappedVector< pg_uuid_t > wires
Flattened child UUID array.
void setInfos(pg_uuid_t token, unsigned info1, unsigned info2)
Update the info1 / info2 annotations of a gate.
MMappedCircuit(const std::string &mp, const std::string &gp, const std::string &wp, const std::string &ep, const std::string &tp, bool read_only)
Delegating constructor that accepts pre-built paths.
std::pair< unsigned, unsigned > getInfos(pg_uuid_t token) const
Return the info1 / info2 pair for the gate token.
static constexpr unsigned long NOTHING
Sentinel returned by operator[]() when the UUID is not present.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
Background worker and IPC primitives for mmap-backed circuit storage.
#define WRITEB_BYTES(ptr, n)
Write n reply bytes to the main-to-background pipe.
#define READM(var, type)
Read one value of type from the background-to-main pipe.
#define WRITEB(pvar, type)
Write one value of type to the main-to-background pipe.
#define READM_BYTES(ptr, n)
Read exactly n bytes of a request from the background-to-main pipe.
Shared-memory segment and inter-process pipe management.
@ gate_rv
Continuous random-variable leaf (extra encodes distribution).
@ gate_annotation
Transparent single-child wrapper carrying a query-level annotation in extra (inversion-free certifica...
@ gate_mobius
Signed Möbius combination: a MEASURE-only gate carrying one integer coefficient per child (in extra,...
@ gate_arith
n-ary arithmetic gate over scalar-valued children (info1 holds operator tag)
@ gate_assumed
Structural marker over a single child whose sub-circuit was computed under a Boolean-provenance assum...
string uuid2string(pg_uuid_t uuid)
Format a pg_uuid_t as a std::string.
C++ utility functions for UUID manipulation.
Per-gate metadata stored in the gates MMappedVector.
unsigned info2
General-purpose integer annotation 2.
unsigned long children_idx
Start index of this gate's children in wires.
unsigned info1
General-purpose integer annotation 1.
unsigned nb_children
Number of children.
Per-relation metadata for the safe-query optimisation.
Oid relid
pg_class OID of the relation (primary key)
AttrNumber block_key[PROVSQL_TABLE_INFO_MAX_BLOCK_KEY]
Block-key column numbers.
uint16_t block_key_n
Number of valid entries in block_key.
Oid ancestors[PROVSQL_TABLE_INFO_MAX_ANCESTORS]
Sorted, deduplicated base-relation OIDs.
uint8_t kind
One of provsql_table_kind.
uint16_t ancestor_n
Number of valid entries in ancestors (0 = no registry info).
UUID structure.