29#include "access/htup_details.h"
30#include "catalog/pg_type.h"
31#include "utils/array.h"
32#include "utils/builtins.h"
33#include "utils/tuplestore.h"
34#include "utils/uuid.h"
55#include <unordered_map>
56#include <unordered_set>
67int checkedArrayLength(ArrayType *arr,
const char *what)
71 if (ARR_NDIM(arr) > 1)
72 provsql_error(
"reachability: %s must be a one-dimensional array", what);
74 provsql_error(
"reachability: %s must not contain NULLs", what);
75 return ARR_NDIM(arr) == 0 ? 0 : ARR_DIMS(arr)[0];
85std::vector<ReachabilityCompiler::EdgeRow> edgesFromArgs(
86 FunctionCallInfo fcinfo,
int block_args_at = -1)
88 ArrayType *srcs = PG_ARGISNULL(0) ? NULL : PG_GETARG_ARRAYTYPE_P(0);
89 ArrayType *dsts = PG_ARGISNULL(1) ? NULL : PG_GETARG_ARRAYTYPE_P(1);
90 ArrayType *tokens = PG_ARGISNULL(2) ? NULL : PG_GETARG_ARRAYTYPE_P(2);
91 ArrayType *probs = PG_ARGISNULL(3) ? NULL : PG_GETARG_ARRAYTYPE_P(3);
92 ArrayType *bkeys = NULL;
93 ArrayType *bidx = NULL;
94 if (block_args_at >= 0) {
95 bkeys = PG_ARGISNULL(block_args_at) ? NULL
96 : PG_GETARG_ARRAYTYPE_P(block_args_at);
97 bidx = PG_ARGISNULL(block_args_at+1) ? NULL
98 : PG_GETARG_ARRAYTYPE_P(block_args_at+1);
101 const int n = checkedArrayLength(srcs,
"sources");
102 if (checkedArrayLength(dsts,
"destinations") != n ||
103 checkedArrayLength(tokens,
"tokens") != n ||
104 checkedArrayLength(probs,
"probabilities") != n)
105 provsql_error(
"reachability: edge arrays must have the same length");
107 (checkedArrayLength(bkeys,
"block keys") != n ||
108 checkedArrayLength(bidx,
"block indices") != n))
109 provsql_error(
"reachability: edge arrays must have the same length");
111 std::vector<ReachabilityCompiler::EdgeRow> rows;
118 const int32 *src_data = (
const int32 *) ARR_DATA_PTR(srcs);
119 const int32 *dst_data = (
const int32 *) ARR_DATA_PTR(dsts);
121 const float8 *prob_data = (
const float8 *) ARR_DATA_PTR(probs);
123 bkeys ? (
const pg_uuid_t *) ARR_DATA_PTR(bkeys) : NULL;
124 const int32 *bidx_data = bidx ? (
const int32 *) ARR_DATA_PTR(bidx) : NULL;
126 for (
int i = 0; i < n; ++i) {
128 row.
src =
static_cast<unsigned long>(src_data[i]);
129 row.
dst =
static_cast<unsigned long>(dst_data[i]);
131 row.
prob = prob_data[i];
133 provsql_error(
"reachability: edge probability %f out of [0,1]",
137 for (
int b = 0; b < 16; ++b)
138 if (bkey_data[i].data[b] != 0)
142 row.
block_index =
static_cast<unsigned>(bidx_data[i]);
145 rows.push_back(std::move(row));
161std::vector<ReachabilityCompiler::SourceArc> sourcesFromArgs(
162 FunctionCallInfo fcinfo,
int base)
164 std::vector<ReachabilityCompiler::SourceArc> sources;
165 ArrayType *sv = PG_ARGISNULL(base) ? NULL : PG_GETARG_ARRAYTYPE_P(base);
166 ArrayType *st = PG_ARGISNULL(base+1) ? NULL : PG_GETARG_ARRAYTYPE_P(base+1);
167 ArrayType *sp = PG_ARGISNULL(base+2) ? NULL : PG_GETARG_ARRAYTYPE_P(base+2);
168 const int ns = checkedArrayLength(sv,
"source vertices");
169 if (checkedArrayLength(st,
"source tokens") != ns ||
170 checkedArrayLength(sp,
"source probabilities") != ns)
171 provsql_error(
"reachability: source arrays must have the same length");
173 provsql_error(
"reachability: at least one source is required");
174 const int32 *v_data = (
const int32 *) ARR_DATA_PTR(sv);
176 const float8 *p_data = (
const float8 *) ARR_DATA_PTR(sp);
178 for (
int i = 0; i < ns; ++i) {
180 sa.
vertex =
static_cast<unsigned long>(v_data[i]);
182 for (
int b = 0; b < 16; ++b)
183 if (t_data[i].data[b] != 0)
190 provsql_error(
"reachability: source probability %f out of [0,1]",
192 sources.push_back(std::move(sa));
209 for (
int i = 4; i < 7; ++i)
211 provsql_error(
"reachability: source, target and directed must not be NULL");
213 auto rows = edgesFromArgs(fcinfo);
214 const unsigned long source =
static_cast<unsigned long>(PG_GETARG_INT32(4));
215 const unsigned long target =
static_cast<unsigned long>(PG_GETARG_INT32(5));
216 const bool directed = PG_GETARG_BOOL(6);
222 "reachability: data treewidth exceeds the supported limit (%d)",
240 auto result = compileFromArgs(fcinfo);
241 PG_RETURN_FLOAT8(result.dd.probabilityEvaluation());
242 }
catch (
const std::exception &e) {
261 auto result = compileFromArgs(fcinfo);
264 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
265 provsql_error(
"reachability_compile_stats: expected composite return type");
266 tupdesc = BlessTupleDesc(tupdesc);
269 bool nulls[6] = {
false,
false,
false,
false,
false,
false};
270 values[0] = Float8GetDatum(result.dd.probabilityEvaluation());
271 values[1] = Int32GetDatum(
static_cast<int32
>(result.stats.data_treewidth));
272 values[2] = Int64GetDatum(
static_cast<int64
>(result.stats.nb_bags));
273 values[3] = Int64GetDatum(
static_cast<int64
>(result.stats.max_states));
274 values[4] = Int64GetDatum(
static_cast<int64
>(result.stats.nb_gates));
275 values[5] = Int64GetDatum(
static_cast<int64
>(result.stats.nb_variables));
277 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
278 }
catch (
const std::exception &e) {
281 provsql_error(
"reachability_compile_stats: unknown exception");
309 auto rows = edgesFromArgs(fcinfo, 4);
310 const bool directed = PG_GETARG_BOOL(9);
315 const auto sources = sourcesFromArgs(fcinfo, 6);
322 "reachability: data treewidth exceeds the supported limit (%d)",
326 std::vector<gate_t> roots;
327 roots.reserve(all.
roots.size());
328 for (
const auto &vr : all.
roots)
329 roots.push_back(vr.root);
332 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
333 MemoryContext per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
334 MemoryContext oldcontext = MemoryContextSwitchTo(per_query_ctx);
337 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
338 MemoryContextSwitchTo(oldcontext);
339 provsql_error(
"reachability_materialize: function must return a row type");
341 tupdesc = BlessTupleDesc(tupdesc);
343 Tuplestorestate *tupstore = tuplestore_begin_heap(
344 rsinfo->allowedModes & SFRM_Materialize_Random,
false, work_mem);
345 rsinfo->returnMode = SFRM_Materialize;
346 rsinfo->setResult = tupstore;
347 rsinfo->setDesc = tupdesc;
349 for (
const auto &vr : all.
roots) {
351 bool nulls[2] = {
false,
false};
352 values[0] = Int32GetDatum(
static_cast<int32
>(vr.vertex));
355 values[1] = UUIDPGetDatum(u);
356 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
359 MemoryContextSwitchTo(oldcontext);
361 }
catch (
const std::exception &e) {
364 provsql_error(
"reachability_materialize: unknown exception");
398 if (PG_ARGISNULL(9) || PG_ARGISNULL(10) || PG_ARGISNULL(11))
400 "reachability: directed, hop_bound and hop_seed must not be NULL");
402 auto rows = edgesFromArgs(fcinfo, 4);
403 const bool directed = PG_GETARG_BOOL(9);
404 const int32 hop_bound = PG_GETARG_INT32(10);
405 const int32 hop_seed = PG_GETARG_INT32(11);
411 const auto sources = sourcesFromArgs(fcinfo, 6);
416 rows, sources, directed,
static_cast<unsigned>(hop_bound));
419 "reachability: data treewidth exceeds the supported limit (%d)",
423 std::vector<gate_t> roots;
425 for (
const auto &vr : all.
roots)
426 roots.push_back(vr.root);
428 roots.push_back(vr.root);
440 std::unordered_map<unsigned long, std::vector<std::string> > by_vertex;
441 for (
const auto &vr : all.
roots)
442 by_vertex[vr.vertex].push_back(
445 auto it = by_vertex.find(vr.vertex);
446 if (it == by_vertex.end() || it->second.size() < 2)
448 std::vector<std::string> texts = it->second;
449 std::sort(texts.begin(), texts.end());
450 std::string name =
"plus-canonical{";
451 for (std::size_t i = 0; i < texts.size(); ++i) {
469 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
470 MemoryContext per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
471 MemoryContext oldcontext = MemoryContextSwitchTo(per_query_ctx);
474 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
475 MemoryContextSwitchTo(oldcontext);
477 "reachability_materialize_hops: function must return a row type");
479 tupdesc = BlessTupleDesc(tupdesc);
481 Tuplestorestate *tupstore = tuplestore_begin_heap(
482 rsinfo->allowedModes & SFRM_Materialize_Random,
false, work_mem);
483 rsinfo->returnMode = SFRM_Materialize;
484 rsinfo->setResult = tupstore;
485 rsinfo->setDesc = tupdesc;
487 for (
const auto &vr : all.
roots) {
489 bool nulls[3] = {
false,
false,
false};
490 values[0] = Int32GetDatum(
static_cast<int32
>(vr.vertex));
491 values[1] = Int32GetDatum(hop_seed +
static_cast<int32
>(vr.hops));
494 values[2] = UUIDPGetDatum(u);
495 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
498 MemoryContextSwitchTo(oldcontext);
500 }
catch (
const std::exception &e) {
501 provsql_error(
"reachability_materialize_hops: %s", e.what());
503 provsql_error(
"reachability_materialize_hops: unknown exception");
532 auto rows = edgesFromArgs(fcinfo, 4);
533 const bool directed = PG_GETARG_BOOL(9);
534 const auto sources = sourcesFromArgs(fcinfo, 6);
536 ArrayType *gids = PG_ARGISNULL(10) ? NULL : PG_GETARG_ARRAYTYPE_P(10);
537 ArrayType *gverts = PG_ARGISNULL(11) ? NULL : PG_GETARG_ARRAYTYPE_P(11);
538 const int ng = checkedArrayLength(gids,
"group ids");
539 if (checkedArrayLength(gverts,
"group member vertices") != ng)
540 provsql_error(
"reachability: group arrays must have the same length");
542 provsql_error(
"reachability: at least one group member is required");
544 const int32 *gid_data = (
const int32 *) ARR_DATA_PTR(gids);
545 const int32 *gv_data = (
const int32 *) ARR_DATA_PTR(gverts);
546 std::map<int32, std::vector<unsigned long> > groups;
547 for (
int i = 0; i < ng; ++i)
548 groups[gid_data[i]].push_back(
static_cast<unsigned long>(gv_data[i]));
549 std::vector<int32> group_ids;
550 std::vector<std::vector<unsigned long> > sets;
551 group_ids.reserve(groups.size());
552 sets.reserve(groups.size());
553 for (
auto &[gid, members] : groups) {
554 group_ids.push_back(gid);
555 sets.push_back(std::move(members));
558 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
559 MemoryContext per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
560 MemoryContext oldcontext = MemoryContextSwitchTo(per_query_ctx);
563 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
564 MemoryContextSwitchTo(oldcontext);
566 "reachability_materialize_any: function must return a row type");
568 tupdesc = BlessTupleDesc(tupdesc);
570 Tuplestorestate *tupstore = tuplestore_begin_heap(
571 rsinfo->allowedModes & SFRM_Materialize_Random,
false, work_mem);
572 rsinfo->returnMode = SFRM_Materialize;
573 rsinfo->setResult = tupstore;
574 rsinfo->setDesc = tupdesc;
585 MemoryContextSwitchTo(oldcontext);
587 "reachability: data treewidth exceeds the supported limit (%d)",
592 for (std::size_t i = 0; i < group_ids.size(); ++i) {
594 bool nulls[2] = {
false,
false};
595 values[0] = Int32GetDatum(group_ids[i]);
598 values[1] = UUIDPGetDatum(u);
599 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
602 MemoryContextSwitchTo(oldcontext);
604 }
catch (
const std::exception &e) {
607 provsql_error(
"reachability_materialize_any: unknown exception");
638 auto rows = edgesFromArgs(fcinfo, 4);
639 const bool directed = PG_GETARG_BOOL(9);
640 const auto sources = sourcesFromArgs(fcinfo, 6);
642 ArrayType *mverts = PG_ARGISNULL(10) ? NULL : PG_GETARG_ARRAYTYPE_P(10);
643 const int nm = checkedArrayLength(mverts,
"member vertices");
645 provsql_error(
"reachability: at least one member vertex is required");
646 const int32 *mv_data = (
const int32 *) ARR_DATA_PTR(mverts);
647 std::vector<unsigned long> set;
649 for (
int i = 0; i < nm; ++i)
650 set.push_back(
static_cast<unsigned long>(mv_data[i]));
658 "reachability: data treewidth exceeds the supported limit (%d)",
666 }
catch (
const std::exception &e) {
667 provsql_error(
"reachability_materialize_cover: %s", e.what());
669 provsql_error(
"reachability_materialize_cover: unknown exception");
constexpr unsigned DNNF_CERT_INFO
d-DNNF certificate value for the (gate-type-specific) per-gate info field.
std::unordered_map< gate_t, pg_uuid_t, hash_gate_t > materializeCertifiedDD(const dDNNF &dd, const std::vector< gate_t > &roots, provsql_route route)
Materialise (the reachable part of) a certified d-D into the mmap store.
pg_uuid_t wrapAssumedAbsorptive(const pg_uuid_t &child)
Wrap a materialised root in the 'absorptive' assumption marker and return the wrapper's UUID.
pg_uuid_t provsqlUuidV5(const std::string &name)
RFC 4122 version-5 UUID in the ProvSQL namespace.
Content-addressed materialisation of a certified d-D into the mmap provenance store.
Decomposition-aligned compilation of two-terminal reachability over bounded-treewidth data into a d-D...
Fix macro conflicts between PostgreSQL headers and the C++ STL/Boost.
static AllHopsResult compileAllHops(const std::vector< EdgeRow > &rows, unsigned long source, bool directed, unsigned hop_bound, std::size_t max_states=DEFAULT_MAX_STATES)
Bounded-hop variant of compileAll(): per-(vertex, exact walk length) circuits for every length up to ...
static Result compile(const std::vector< EdgeRow > &rows, unsigned long source, unsigned long target, bool directed, std::size_t max_states=DEFAULT_MAX_STATES)
Compile s-t reachability over rows into a d-D.
static constexpr unsigned MAX_HOP_BOUND
Maximum supported hop bound for compileAllHops().
static AnyReachAllResult compileAnyReachAll(const std::vector< EdgeRow > &rows, const std::vector< SourceArc > &sources, const std::vector< std::vector< unsigned long > > &sets, bool directed, std::size_t max_states=DEFAULT_MAX_STATES)
Multi-set variant of compileAnyReach(): one shared circuit, one root per target set.
static AnyReachAllResult compileCoverReachAll(const std::vector< EdgeRow > &rows, const std::vector< SourceArc > &sources, const std::vector< std::vector< unsigned long > > &sets, bool directed, std::size_t max_states=DEFAULT_MAX_STATES)
Multi-set variant of compileCoverReach(): one shared (content-deduplicated) circuit,...
static AllResult compileAll(const std::vector< EdgeRow > &rows, unsigned long source, bool directed, std::size_t max_states=DEFAULT_MAX_STATES)
Compile the reachability circuits of every vertex in one pass.
Exception thrown when a tree decomposition cannot be constructed.
static constexpr int MAX_TREEWIDTH
Maximum supported treewidth.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
void provsql_internal_create_gate(const pg_uuid_t *token, gate_type type, unsigned nb_children, const pg_uuid_t *children_data)
Internal entry point behind create_gate(): cache + worker IPC.
void provsql_internal_set_infos(const pg_uuid_t *token, unsigned info1, unsigned info2)
Internal entry point behind set_infos(): worker IPC only.
Background worker and IPC primitives for mmap-backed circuit storage.
Shared-memory segment and inter-process pipe management.
Core types, constants, and utilities shared across ProvSQL.
@ PROVSQL_ROUTE_REACHABILITY
Recursive-reachability compiler (src/reachability_evaluate.cpp).
string uuid2string(pg_uuid_t uuid)
Format a pg_uuid_t as a std::string.
C++ utility functions for UUID manipulation.
Datum reachability_materialize_cover(PG_FUNCTION_ARGS)
PostgreSQL-callable entry point: "every member vertex reachable" (k-terminal / coverage) compil...
Datum reachability_materialize_any(PG_FUNCTION_ARGS)
PostgreSQL-callable entry point: per-group "some member reachable" compilation and materialisat...
Datum reachability_materialize(PG_FUNCTION_ARGS)
PostgreSQL-callable entry point: all-targets compilation and materialisation.
Datum reachability_compile_stats(PG_FUNCTION_ARGS)
PostgreSQL-callable entry point: probability plus compilation statistics.
Datum reachability_materialize_hops(PG_FUNCTION_ARGS)
PostgreSQL-callable entry point: bounded-hop all-targets compilation and materialisation.
Datum reachability_evaluate(PG_FUNCTION_ARGS)
PostgreSQL-callable entry point: exact reachability probability.
A bounded-hop all-targets compilation.
std::vector< VertexHopRoot > roots
Per (vertex, exact length) roots.
std::vector< VertexRoot > within_roots
Per-vertex "within the bound" roots.
An all-targets compilation: one shared d-D, one root per reachable vertex.
std::vector< VertexRoot > roots
One entry per vertex reachable in the all-edges-present world (including the source itself,...
dDNNF dd
Shared circuit (gates are reused across vertices).
A multi-set any-reach compilation: one shared circuit, one root per target set.
dDNNF dd
Shared circuit (consed: identical subcircuits are the same gate).
std::vector< gate_t > roots
One root per input set, in input order.
One row of the edge relation.
std::string token
Provenance token (UUID) of the edge tuple.
std::string block_key
Block-independent (BID) key variable (UUID) when the tuple is a mulinput alternative (e....
unsigned long src
Source vertex ID.
unsigned long dst
Destination vertex ID.
double prob
Probability of the edge tuple.
unsigned block_index
Outcome index within the block (the mulinput gate's info).
A compiled reachability query: the d-D and its statistics.
One source of a multi-source compilation.
bool certain
Always-present source (no gating variable).
std::string token
Provenance token of the source tuple (unused when certain).
double prob
Source-tuple probability (unused when certain).
unsigned long vertex
Source vertex.