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);
439 std::unordered_map<unsigned long, std::vector<std::string> > by_vertex;
440 for (
const auto &vr : all.
roots)
441 by_vertex[vr.vertex].push_back(
444 auto it = by_vertex.find(vr.vertex);
445 if (it == by_vertex.end() || it->second.size() < 2)
447 std::vector<pg_uuid_t> tokens;
448 for (
const auto &t : it->second)
456 static_cast<int>(tokens.size()), &within,
461 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
462 MemoryContext per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
463 MemoryContext oldcontext = MemoryContextSwitchTo(per_query_ctx);
466 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
467 MemoryContextSwitchTo(oldcontext);
469 "reachability_materialize_hops: function must return a row type");
471 tupdesc = BlessTupleDesc(tupdesc);
473 Tuplestorestate *tupstore = tuplestore_begin_heap(
474 rsinfo->allowedModes & SFRM_Materialize_Random,
false, work_mem);
475 rsinfo->returnMode = SFRM_Materialize;
476 rsinfo->setResult = tupstore;
477 rsinfo->setDesc = tupdesc;
479 for (
const auto &vr : all.
roots) {
481 bool nulls[3] = {
false,
false,
false};
482 values[0] = Int32GetDatum(
static_cast<int32
>(vr.vertex));
483 values[1] = Int32GetDatum(hop_seed +
static_cast<int32
>(vr.hops));
486 values[2] = UUIDPGetDatum(u);
487 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
490 MemoryContextSwitchTo(oldcontext);
492 }
catch (
const std::exception &e) {
493 provsql_error(
"reachability_materialize_hops: %s", e.what());
495 provsql_error(
"reachability_materialize_hops: unknown exception");
524 auto rows = edgesFromArgs(fcinfo, 4);
525 const bool directed = PG_GETARG_BOOL(9);
526 const auto sources = sourcesFromArgs(fcinfo, 6);
528 ArrayType *gids = PG_ARGISNULL(10) ? NULL : PG_GETARG_ARRAYTYPE_P(10);
529 ArrayType *gverts = PG_ARGISNULL(11) ? NULL : PG_GETARG_ARRAYTYPE_P(11);
530 const int ng = checkedArrayLength(gids,
"group ids");
531 if (checkedArrayLength(gverts,
"group member vertices") != ng)
532 provsql_error(
"reachability: group arrays must have the same length");
534 provsql_error(
"reachability: at least one group member is required");
536 const int32 *gid_data = (
const int32 *) ARR_DATA_PTR(gids);
537 const int32 *gv_data = (
const int32 *) ARR_DATA_PTR(gverts);
538 std::map<int32, std::vector<unsigned long> > groups;
539 for (
int i = 0; i < ng; ++i)
540 groups[gid_data[i]].push_back(
static_cast<unsigned long>(gv_data[i]));
541 std::vector<int32> group_ids;
542 std::vector<std::vector<unsigned long> > sets;
543 group_ids.reserve(groups.size());
544 sets.reserve(groups.size());
545 for (
auto &[gid, members] : groups) {
546 group_ids.push_back(gid);
547 sets.push_back(std::move(members));
550 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
551 MemoryContext per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
552 MemoryContext oldcontext = MemoryContextSwitchTo(per_query_ctx);
555 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
556 MemoryContextSwitchTo(oldcontext);
558 "reachability_materialize_any: function must return a row type");
560 tupdesc = BlessTupleDesc(tupdesc);
562 Tuplestorestate *tupstore = tuplestore_begin_heap(
563 rsinfo->allowedModes & SFRM_Materialize_Random,
false, work_mem);
564 rsinfo->returnMode = SFRM_Materialize;
565 rsinfo->setResult = tupstore;
566 rsinfo->setDesc = tupdesc;
577 MemoryContextSwitchTo(oldcontext);
579 "reachability: data treewidth exceeds the supported limit (%d)",
584 for (std::size_t i = 0; i < group_ids.size(); ++i) {
586 bool nulls[2] = {
false,
false};
587 values[0] = Int32GetDatum(group_ids[i]);
590 values[1] = UUIDPGetDatum(u);
591 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
594 MemoryContextSwitchTo(oldcontext);
596 }
catch (
const std::exception &e) {
599 provsql_error(
"reachability_materialize_any: unknown exception");
630 auto rows = edgesFromArgs(fcinfo, 4);
631 const bool directed = PG_GETARG_BOOL(9);
632 const auto sources = sourcesFromArgs(fcinfo, 6);
634 ArrayType *mverts = PG_ARGISNULL(10) ? NULL : PG_GETARG_ARRAYTYPE_P(10);
635 const int nm = checkedArrayLength(mverts,
"member vertices");
637 provsql_error(
"reachability: at least one member vertex is required");
638 const int32 *mv_data = (
const int32 *) ARR_DATA_PTR(mverts);
639 std::vector<unsigned long> set;
641 for (
int i = 0; i < nm; ++i)
642 set.push_back(
static_cast<unsigned long>(mv_data[i]));
650 "reachability: data treewidth exceeds the supported limit (%d)",
658 }
catch (
const std::exception &e) {
659 provsql_error(
"reachability_materialize_cover: %s", e.what());
661 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.
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.
pg_uuid_t provsql_plant_canonical(const char *work_name, gate_type type, const pg_uuid_t *children, int n, const pg_uuid_t *target, unsigned info1, unsigned info2)
Pre-create ("plant") a gate at the canonical address of a multiset of tokens, for the working table o...
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
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).
pg_uuid_t string2uuid(const string &source)
Parse a UUID string into a pg_uuid_t.
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.