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/uuid.h"
54#include <unordered_map>
55#include <unordered_set>
66int checkedArrayLength(ArrayType *arr,
const char *what)
70 if (ARR_NDIM(arr) > 1)
71 provsql_error(
"reachability: %s must be a one-dimensional array", what);
73 provsql_error(
"reachability: %s must not contain NULLs", what);
74 return ARR_NDIM(arr) == 0 ? 0 : ARR_DIMS(arr)[0];
84std::vector<ReachabilityCompiler::EdgeRow> edgesFromArgs(
85 FunctionCallInfo fcinfo,
int block_args_at = -1)
87 ArrayType *srcs = PG_ARGISNULL(0) ? NULL : PG_GETARG_ARRAYTYPE_P(0);
88 ArrayType *dsts = PG_ARGISNULL(1) ? NULL : PG_GETARG_ARRAYTYPE_P(1);
89 ArrayType *tokens = PG_ARGISNULL(2) ? NULL : PG_GETARG_ARRAYTYPE_P(2);
90 ArrayType *probs = PG_ARGISNULL(3) ? NULL : PG_GETARG_ARRAYTYPE_P(3);
91 ArrayType *bkeys = NULL;
92 ArrayType *bidx = NULL;
93 if (block_args_at >= 0) {
94 bkeys = PG_ARGISNULL(block_args_at) ? NULL
95 : PG_GETARG_ARRAYTYPE_P(block_args_at);
96 bidx = PG_ARGISNULL(block_args_at+1) ? NULL
97 : PG_GETARG_ARRAYTYPE_P(block_args_at+1);
100 const int n = checkedArrayLength(srcs,
"sources");
101 if (checkedArrayLength(dsts,
"destinations") != n ||
102 checkedArrayLength(tokens,
"tokens") != n ||
103 checkedArrayLength(probs,
"probabilities") != n)
104 provsql_error(
"reachability: edge arrays must have the same length");
106 (checkedArrayLength(bkeys,
"block keys") != n ||
107 checkedArrayLength(bidx,
"block indices") != n))
108 provsql_error(
"reachability: edge arrays must have the same length");
110 std::vector<ReachabilityCompiler::EdgeRow> rows;
117 const int32 *src_data = (
const int32 *) ARR_DATA_PTR(srcs);
118 const int32 *dst_data = (
const int32 *) ARR_DATA_PTR(dsts);
120 const float8 *prob_data = (
const float8 *) ARR_DATA_PTR(probs);
122 bkeys ? (
const pg_uuid_t *) ARR_DATA_PTR(bkeys) : NULL;
123 const int32 *bidx_data = bidx ? (
const int32 *) ARR_DATA_PTR(bidx) : NULL;
125 for (
int i = 0; i < n; ++i) {
127 row.
src =
static_cast<unsigned long>(src_data[i]);
128 row.
dst =
static_cast<unsigned long>(dst_data[i]);
130 row.
prob = prob_data[i];
132 provsql_error(
"reachability: edge probability %f out of [0,1]",
136 for (
int b = 0; b < 16; ++b)
137 if (bkey_data[i].data[b] != 0)
141 row.
block_index =
static_cast<unsigned>(bidx_data[i]);
144 rows.push_back(std::move(row));
160std::vector<ReachabilityCompiler::SourceArc> sourcesFromArgs(
161 FunctionCallInfo fcinfo,
int base)
163 std::vector<ReachabilityCompiler::SourceArc> sources;
164 ArrayType *sv = PG_ARGISNULL(base) ? NULL : PG_GETARG_ARRAYTYPE_P(base);
165 ArrayType *st = PG_ARGISNULL(base+1) ? NULL : PG_GETARG_ARRAYTYPE_P(base+1);
166 ArrayType *sp = PG_ARGISNULL(base+2) ? NULL : PG_GETARG_ARRAYTYPE_P(base+2);
167 const int ns = checkedArrayLength(sv,
"source vertices");
168 if (checkedArrayLength(st,
"source tokens") != ns ||
169 checkedArrayLength(sp,
"source probabilities") != ns)
170 provsql_error(
"reachability: source arrays must have the same length");
172 provsql_error(
"reachability: at least one source is required");
173 const int32 *v_data = (
const int32 *) ARR_DATA_PTR(sv);
175 const float8 *p_data = (
const float8 *) ARR_DATA_PTR(sp);
177 for (
int i = 0; i < ns; ++i) {
179 sa.
vertex =
static_cast<unsigned long>(v_data[i]);
181 for (
int b = 0; b < 16; ++b)
182 if (t_data[i].data[b] != 0)
189 provsql_error(
"reachability: source probability %f out of [0,1]",
191 sources.push_back(std::move(sa));
208 for (
int i = 4; i < 7; ++i)
210 provsql_error(
"reachability: source, target and directed must not be NULL");
212 auto rows = edgesFromArgs(fcinfo);
213 const unsigned long source =
static_cast<unsigned long>(PG_GETARG_INT32(4));
214 const unsigned long target =
static_cast<unsigned long>(PG_GETARG_INT32(5));
215 const bool directed = PG_GETARG_BOOL(6);
221 "reachability: data treewidth exceeds the supported limit (%d)",
239 auto result = compileFromArgs(fcinfo);
240 PG_RETURN_FLOAT8(result.dd.probabilityEvaluation());
241 }
catch (
const std::exception &e) {
260 auto result = compileFromArgs(fcinfo);
263 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
264 provsql_error(
"reachability_compile_stats: expected composite return type");
265 tupdesc = BlessTupleDesc(tupdesc);
268 bool nulls[6] = {
false,
false,
false,
false,
false,
false};
269 values[0] = Float8GetDatum(result.dd.probabilityEvaluation());
270 values[1] = Int32GetDatum(
static_cast<int32
>(result.stats.data_treewidth));
271 values[2] = Int64GetDatum(
static_cast<int64
>(result.stats.nb_bags));
272 values[3] = Int64GetDatum(
static_cast<int64
>(result.stats.max_states));
273 values[4] = Int64GetDatum(
static_cast<int64
>(result.stats.nb_gates));
274 values[5] = Int64GetDatum(
static_cast<int64
>(result.stats.nb_variables));
276 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
277 }
catch (
const std::exception &e) {
280 provsql_error(
"reachability_compile_stats: unknown exception");
308 auto rows = edgesFromArgs(fcinfo, 4);
309 const bool directed = PG_GETARG_BOOL(9);
314 const auto sources = sourcesFromArgs(fcinfo, 6);
321 "reachability: data treewidth exceeds the supported limit (%d)",
325 std::vector<gate_t> roots;
326 roots.reserve(all.
roots.size());
327 for (
const auto &vr : all.
roots)
328 roots.push_back(vr.root);
331 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
332 MemoryContext per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
333 MemoryContext oldcontext = MemoryContextSwitchTo(per_query_ctx);
336 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
337 MemoryContextSwitchTo(oldcontext);
338 provsql_error(
"reachability_materialize: function must return a row type");
340 tupdesc = BlessTupleDesc(tupdesc);
342 Tuplestorestate *tupstore = tuplestore_begin_heap(
343 rsinfo->allowedModes & SFRM_Materialize_Random,
false, work_mem);
344 rsinfo->returnMode = SFRM_Materialize;
345 rsinfo->setResult = tupstore;
346 rsinfo->setDesc = tupdesc;
348 for (
const auto &vr : all.
roots) {
350 bool nulls[2] = {
false,
false};
351 values[0] = Int32GetDatum(
static_cast<int32
>(vr.vertex));
354 values[1] = UUIDPGetDatum(u);
355 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
358 MemoryContextSwitchTo(oldcontext);
360 }
catch (
const std::exception &e) {
363 provsql_error(
"reachability_materialize: unknown exception");
397 if (PG_ARGISNULL(9) || PG_ARGISNULL(10) || PG_ARGISNULL(11))
399 "reachability: directed, hop_bound and hop_seed must not be NULL");
401 auto rows = edgesFromArgs(fcinfo, 4);
402 const bool directed = PG_GETARG_BOOL(9);
403 const int32 hop_bound = PG_GETARG_INT32(10);
404 const int32 hop_seed = PG_GETARG_INT32(11);
410 const auto sources = sourcesFromArgs(fcinfo, 6);
415 rows, sources, directed,
static_cast<unsigned>(hop_bound));
418 "reachability: data treewidth exceeds the supported limit (%d)",
422 std::vector<gate_t> roots;
424 for (
const auto &vr : all.
roots)
425 roots.push_back(vr.root);
427 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<std::string> texts = it->second;
448 std::sort(texts.begin(), texts.end());
449 std::string name =
"plus-canonical{";
450 for (std::size_t i = 0; i < texts.size(); ++i) {
464 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
465 MemoryContext per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
466 MemoryContext oldcontext = MemoryContextSwitchTo(per_query_ctx);
469 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
470 MemoryContextSwitchTo(oldcontext);
472 "reachability_materialize_hops: function must return a row type");
474 tupdesc = BlessTupleDesc(tupdesc);
476 Tuplestorestate *tupstore = tuplestore_begin_heap(
477 rsinfo->allowedModes & SFRM_Materialize_Random,
false, work_mem);
478 rsinfo->returnMode = SFRM_Materialize;
479 rsinfo->setResult = tupstore;
480 rsinfo->setDesc = tupdesc;
482 for (
const auto &vr : all.
roots) {
484 bool nulls[3] = {
false,
false,
false};
485 values[0] = Int32GetDatum(
static_cast<int32
>(vr.vertex));
486 values[1] = Int32GetDatum(hop_seed +
static_cast<int32
>(vr.hops));
489 values[2] = UUIDPGetDatum(u);
490 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
493 MemoryContextSwitchTo(oldcontext);
495 }
catch (
const std::exception &e) {
496 provsql_error(
"reachability_materialize_hops: %s", e.what());
498 provsql_error(
"reachability_materialize_hops: unknown exception");
527 auto rows = edgesFromArgs(fcinfo, 4);
528 const bool directed = PG_GETARG_BOOL(9);
529 const auto sources = sourcesFromArgs(fcinfo, 6);
531 ArrayType *gids = PG_ARGISNULL(10) ? NULL : PG_GETARG_ARRAYTYPE_P(10);
532 ArrayType *gverts = PG_ARGISNULL(11) ? NULL : PG_GETARG_ARRAYTYPE_P(11);
533 const int ng = checkedArrayLength(gids,
"group ids");
534 if (checkedArrayLength(gverts,
"group member vertices") != ng)
535 provsql_error(
"reachability: group arrays must have the same length");
537 provsql_error(
"reachability: at least one group member is required");
539 const int32 *gid_data = (
const int32 *) ARR_DATA_PTR(gids);
540 const int32 *gv_data = (
const int32 *) ARR_DATA_PTR(gverts);
541 std::map<int32, std::vector<unsigned long> > groups;
542 for (
int i = 0; i < ng; ++i)
543 groups[gid_data[i]].push_back(
static_cast<unsigned long>(gv_data[i]));
544 std::vector<int32> group_ids;
545 std::vector<std::vector<unsigned long> > sets;
546 group_ids.reserve(groups.size());
547 sets.reserve(groups.size());
548 for (
auto &[gid, members] : groups) {
549 group_ids.push_back(gid);
550 sets.push_back(std::move(members));
553 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
554 MemoryContext per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
555 MemoryContext oldcontext = MemoryContextSwitchTo(per_query_ctx);
558 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
559 MemoryContextSwitchTo(oldcontext);
561 "reachability_materialize_any: function must return a row type");
563 tupdesc = BlessTupleDesc(tupdesc);
565 Tuplestorestate *tupstore = tuplestore_begin_heap(
566 rsinfo->allowedModes & SFRM_Materialize_Random,
false, work_mem);
567 rsinfo->returnMode = SFRM_Materialize;
568 rsinfo->setResult = tupstore;
569 rsinfo->setDesc = tupdesc;
580 MemoryContextSwitchTo(oldcontext);
582 "reachability: data treewidth exceeds the supported limit (%d)",
587 for (std::size_t i = 0; i < group_ids.size(); ++i) {
589 bool nulls[2] = {
false,
false};
590 values[0] = Int32GetDatum(group_ids[i]);
593 values[1] = UUIDPGetDatum(u);
594 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
597 MemoryContextSwitchTo(oldcontext);
599 }
catch (
const std::exception &e) {
602 provsql_error(
"reachability_materialize_any: unknown exception");
633 auto rows = edgesFromArgs(fcinfo, 4);
634 const bool directed = PG_GETARG_BOOL(9);
635 const auto sources = sourcesFromArgs(fcinfo, 6);
637 ArrayType *mverts = PG_ARGISNULL(10) ? NULL : PG_GETARG_ARRAYTYPE_P(10);
638 const int nm = checkedArrayLength(mverts,
"member vertices");
640 provsql_error(
"reachability: at least one member vertex is required");
641 const int32 *mv_data = (
const int32 *) ARR_DATA_PTR(mverts);
642 std::vector<unsigned long> set;
644 for (
int i = 0; i < nm; ++i)
645 set.push_back(
static_cast<unsigned long>(mv_data[i]));
653 "reachability: data treewidth exceeds the supported limit (%d)",
661 }
catch (
const std::exception &e) {
662 provsql_error(
"reachability_materialize_cover: %s", e.what());
664 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)
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.
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.